blob: c062b4f666e3da122094507f17d26f1cf64751a7 [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;
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +020081 struct list_head nexthop_list;
Ido Schimmel9011b672017-05-16 19:38:25 +020082 struct {
83 struct mlxsw_sp_lpm_tree *trees;
84 unsigned int tree_count;
85 } lpm;
86 struct {
87 struct delayed_work dw;
88 unsigned long interval; /* ms */
89 } neighs_update;
90 struct delayed_work nexthop_probe_dw;
91#define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */
92 struct list_head nexthop_neighs_list;
Petr Machata1012b9a2017-09-02 23:49:23 +020093 struct list_head ipip_list;
Ido Schimmel9011b672017-05-16 19:38:25 +020094 bool aborted;
Ido Schimmel7e39d112017-05-16 19:38:28 +020095 struct notifier_block fib_nb;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020096 const struct mlxsw_sp_rif_ops **rif_ops_arr;
Petr Machata38ebc0f2017-09-02 23:49:17 +020097 const struct mlxsw_sp_ipip_ops **ipip_ops_arr;
Ido Schimmel9011b672017-05-16 19:38:25 +020098};
99
Ido Schimmel4724ba562017-03-10 08:53:39 +0100100struct mlxsw_sp_rif {
101 struct list_head nexthop_list;
102 struct list_head neigh_list;
103 struct net_device *dev;
Ido Schimmela1107482017-05-26 08:37:39 +0200104 struct mlxsw_sp_fid *fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100105 unsigned char addr[ETH_ALEN];
106 int mtu;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100107 u16 rif_index;
Ido Schimmel69132292017-03-10 08:53:42 +0100108 u16 vr_id;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200109 const struct mlxsw_sp_rif_ops *ops;
110 struct mlxsw_sp *mlxsw_sp;
111
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200112 unsigned int counter_ingress;
113 bool counter_ingress_valid;
114 unsigned int counter_egress;
115 bool counter_egress_valid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100116};
117
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200118struct mlxsw_sp_rif_params {
119 struct net_device *dev;
120 union {
121 u16 system_port;
122 u16 lag_id;
123 };
124 u16 vid;
125 bool lag;
126};
127
Ido Schimmel4d93cee2017-05-26 08:37:34 +0200128struct mlxsw_sp_rif_subport {
129 struct mlxsw_sp_rif common;
130 union {
131 u16 system_port;
132 u16 lag_id;
133 };
134 u16 vid;
135 bool lag;
136};
137
Petr Machata6ddb7422017-09-02 23:49:19 +0200138struct mlxsw_sp_rif_ipip_lb {
139 struct mlxsw_sp_rif common;
140 struct mlxsw_sp_rif_ipip_lb_config lb_config;
141 u16 ul_vr_id; /* Reserved for Spectrum-2. */
142};
143
144struct mlxsw_sp_rif_params_ipip_lb {
145 struct mlxsw_sp_rif_params common;
146 struct mlxsw_sp_rif_ipip_lb_config lb_config;
147};
148
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200149struct mlxsw_sp_rif_ops {
150 enum mlxsw_sp_rif_type type;
151 size_t rif_size;
152
153 void (*setup)(struct mlxsw_sp_rif *rif,
154 const struct mlxsw_sp_rif_params *params);
155 int (*configure)(struct mlxsw_sp_rif *rif);
156 void (*deconfigure)(struct mlxsw_sp_rif *rif);
157 struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif);
158};
159
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200160static unsigned int *
161mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
162 enum mlxsw_sp_rif_counter_dir dir)
163{
164 switch (dir) {
165 case MLXSW_SP_RIF_COUNTER_EGRESS:
166 return &rif->counter_egress;
167 case MLXSW_SP_RIF_COUNTER_INGRESS:
168 return &rif->counter_ingress;
169 }
170 return NULL;
171}
172
173static bool
174mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
175 enum mlxsw_sp_rif_counter_dir dir)
176{
177 switch (dir) {
178 case MLXSW_SP_RIF_COUNTER_EGRESS:
179 return rif->counter_egress_valid;
180 case MLXSW_SP_RIF_COUNTER_INGRESS:
181 return rif->counter_ingress_valid;
182 }
183 return false;
184}
185
186static void
187mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
188 enum mlxsw_sp_rif_counter_dir dir,
189 bool valid)
190{
191 switch (dir) {
192 case MLXSW_SP_RIF_COUNTER_EGRESS:
193 rif->counter_egress_valid = valid;
194 break;
195 case MLXSW_SP_RIF_COUNTER_INGRESS:
196 rif->counter_ingress_valid = valid;
197 break;
198 }
199}
200
201static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
202 unsigned int counter_index, bool enable,
203 enum mlxsw_sp_rif_counter_dir dir)
204{
205 char ritr_pl[MLXSW_REG_RITR_LEN];
206 bool is_egress = false;
207 int err;
208
209 if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
210 is_egress = true;
211 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
212 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
213 if (err)
214 return err;
215
216 mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
217 is_egress);
218 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
219}
220
221int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
222 struct mlxsw_sp_rif *rif,
223 enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
224{
225 char ricnt_pl[MLXSW_REG_RICNT_LEN];
226 unsigned int *p_counter_index;
227 bool valid;
228 int err;
229
230 valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
231 if (!valid)
232 return -EINVAL;
233
234 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
235 if (!p_counter_index)
236 return -EINVAL;
237 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
238 MLXSW_REG_RICNT_OPCODE_NOP);
239 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
240 if (err)
241 return err;
242 *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
243 return 0;
244}
245
246static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
247 unsigned int counter_index)
248{
249 char ricnt_pl[MLXSW_REG_RICNT_LEN];
250
251 mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
252 MLXSW_REG_RICNT_OPCODE_CLEAR);
253 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
254}
255
256int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
257 struct mlxsw_sp_rif *rif,
258 enum mlxsw_sp_rif_counter_dir dir)
259{
260 unsigned int *p_counter_index;
261 int err;
262
263 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
264 if (!p_counter_index)
265 return -EINVAL;
266 err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
267 p_counter_index);
268 if (err)
269 return err;
270
271 err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
272 if (err)
273 goto err_counter_clear;
274
275 err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
276 *p_counter_index, true, dir);
277 if (err)
278 goto err_counter_edit;
279 mlxsw_sp_rif_counter_valid_set(rif, dir, true);
280 return 0;
281
282err_counter_edit:
283err_counter_clear:
284 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
285 *p_counter_index);
286 return err;
287}
288
289void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
290 struct mlxsw_sp_rif *rif,
291 enum mlxsw_sp_rif_counter_dir dir)
292{
293 unsigned int *p_counter_index;
294
Arkadi Sharshevsky6b1206b2017-05-18 09:18:53 +0200295 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
296 return;
297
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200298 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
299 if (WARN_ON(!p_counter_index))
300 return;
301 mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
302 *p_counter_index, false, dir);
303 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
304 *p_counter_index);
305 mlxsw_sp_rif_counter_valid_set(rif, dir, false);
306}
307
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200308static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)
309{
310 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
311 struct devlink *devlink;
312
313 devlink = priv_to_devlink(mlxsw_sp->core);
314 if (!devlink_dpipe_table_counter_enabled(devlink,
315 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))
316 return;
317 mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
318}
319
320static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
321{
322 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
323
324 mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
325}
326
Ido Schimmel4724ba562017-03-10 08:53:39 +0100327static struct mlxsw_sp_rif *
328mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
329 const struct net_device *dev);
330
Ido Schimmel7dcc18a2017-07-18 10:10:30 +0200331#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
Ido Schimmel9011b672017-05-16 19:38:25 +0200332
333struct mlxsw_sp_prefix_usage {
334 DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
335};
336
Jiri Pirko53342022016-07-04 08:23:08 +0200337#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
338 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
339
340static bool
341mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
342 struct mlxsw_sp_prefix_usage *prefix_usage2)
343{
344 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
345}
346
Jiri Pirko6b75c482016-07-04 08:23:09 +0200347static bool
348mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
349{
350 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
351
352 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
353}
354
355static void
356mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
357 struct mlxsw_sp_prefix_usage *prefix_usage2)
358{
359 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
360}
361
362static void
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200363mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
364 unsigned char prefix_len)
365{
366 set_bit(prefix_len, prefix_usage->b);
367}
368
369static void
370mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
371 unsigned char prefix_len)
372{
373 clear_bit(prefix_len, prefix_usage->b);
374}
375
376struct mlxsw_sp_fib_key {
377 unsigned char addr[sizeof(struct in6_addr)];
378 unsigned char prefix_len;
379};
380
Jiri Pirko61c503f2016-07-04 08:23:11 +0200381enum mlxsw_sp_fib_entry_type {
382 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
383 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
384 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
Petr Machata4607f6d2017-09-02 23:49:25 +0200385
386 /* This is a special case of local delivery, where a packet should be
387 * decapsulated on reception. Note that there is no corresponding ENCAP,
388 * because that's a type of next hop, not of FIB entry. (There can be
389 * several next hops in a REMOTE entry, and some of them may be
390 * encapsulating entries.)
391 */
392 MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP,
Jiri Pirko61c503f2016-07-04 08:23:11 +0200393};
394
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200395struct mlxsw_sp_nexthop_group;
Ido Schimmel9011b672017-05-16 19:38:25 +0200396struct mlxsw_sp_fib;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200397
Ido Schimmel9aecce12017-02-09 10:28:42 +0100398struct mlxsw_sp_fib_node {
399 struct list_head entry_list;
Jiri Pirkob45f64d2016-09-26 12:52:31 +0200400 struct list_head list;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100401 struct rhash_head ht_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100402 struct mlxsw_sp_fib *fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100403 struct mlxsw_sp_fib_key key;
404};
405
Petr Machata4607f6d2017-09-02 23:49:25 +0200406struct mlxsw_sp_fib_entry_decap {
407 struct mlxsw_sp_ipip_entry *ipip_entry;
408 u32 tunnel_index;
409};
410
Ido Schimmel9aecce12017-02-09 10:28:42 +0100411struct mlxsw_sp_fib_entry {
412 struct list_head list;
413 struct mlxsw_sp_fib_node *fib_node;
414 enum mlxsw_sp_fib_entry_type type;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200415 struct list_head nexthop_group_node;
416 struct mlxsw_sp_nexthop_group *nh_group;
Petr Machata4607f6d2017-09-02 23:49:25 +0200417 struct mlxsw_sp_fib_entry_decap decap; /* Valid for decap entries. */
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200418};
419
Ido Schimmel4f1c7f12017-07-18 10:10:26 +0200420struct mlxsw_sp_fib4_entry {
421 struct mlxsw_sp_fib_entry common;
422 u32 tb_id;
423 u32 prio;
424 u8 tos;
425 u8 type;
426};
427
Ido Schimmel428b8512017-08-03 13:28:28 +0200428struct mlxsw_sp_fib6_entry {
429 struct mlxsw_sp_fib_entry common;
430 struct list_head rt6_list;
431 unsigned int nrt6;
432};
433
434struct mlxsw_sp_rt6 {
435 struct list_head list;
436 struct rt6_info *rt;
437};
438
Ido Schimmel9011b672017-05-16 19:38:25 +0200439struct mlxsw_sp_lpm_tree {
440 u8 id; /* tree ID */
441 unsigned int ref_count;
442 enum mlxsw_sp_l3proto proto;
443 struct mlxsw_sp_prefix_usage prefix_usage;
444};
445
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200446struct mlxsw_sp_fib {
447 struct rhashtable ht;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100448 struct list_head node_list;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100449 struct mlxsw_sp_vr *vr;
450 struct mlxsw_sp_lpm_tree *lpm_tree;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200451 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
452 struct mlxsw_sp_prefix_usage prefix_usage;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100453 enum mlxsw_sp_l3proto proto;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200454};
455
Ido Schimmel9011b672017-05-16 19:38:25 +0200456struct mlxsw_sp_vr {
457 u16 id; /* virtual router ID */
458 u32 tb_id; /* kernel fib table id */
459 unsigned int rif_count;
460 struct mlxsw_sp_fib *fib4;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200461 struct mlxsw_sp_fib *fib6;
Ido Schimmel9011b672017-05-16 19:38:25 +0200462};
463
Ido Schimmel9aecce12017-02-09 10:28:42 +0100464static const struct rhashtable_params mlxsw_sp_fib_ht_params;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200465
Ido Schimmel76610eb2017-03-10 08:53:41 +0100466static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
467 enum mlxsw_sp_l3proto proto)
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200468{
469 struct mlxsw_sp_fib *fib;
470 int err;
471
472 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
473 if (!fib)
474 return ERR_PTR(-ENOMEM);
475 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
476 if (err)
477 goto err_rhashtable_init;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100478 INIT_LIST_HEAD(&fib->node_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100479 fib->proto = proto;
480 fib->vr = vr;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200481 return fib;
482
483err_rhashtable_init:
484 kfree(fib);
485 return ERR_PTR(err);
486}
487
488static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
489{
Ido Schimmel9aecce12017-02-09 10:28:42 +0100490 WARN_ON(!list_empty(&fib->node_list));
Ido Schimmel76610eb2017-03-10 08:53:41 +0100491 WARN_ON(fib->lpm_tree);
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200492 rhashtable_destroy(&fib->ht);
493 kfree(fib);
494}
495
Jiri Pirko53342022016-07-04 08:23:08 +0200496static struct mlxsw_sp_lpm_tree *
Ido Schimmel382dbb42017-03-10 08:53:40 +0100497mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200498{
499 static struct mlxsw_sp_lpm_tree *lpm_tree;
500 int i;
501
Ido Schimmel9011b672017-05-16 19:38:25 +0200502 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
503 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Ido Schimmel382dbb42017-03-10 08:53:40 +0100504 if (lpm_tree->ref_count == 0)
505 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200506 }
507 return NULL;
508}
509
510static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
511 struct mlxsw_sp_lpm_tree *lpm_tree)
512{
513 char ralta_pl[MLXSW_REG_RALTA_LEN];
514
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200515 mlxsw_reg_ralta_pack(ralta_pl, true,
516 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
517 lpm_tree->id);
Jiri Pirko53342022016-07-04 08:23:08 +0200518 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
519}
520
Ido Schimmelcc702672017-08-14 10:54:03 +0200521static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
522 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200523{
524 char ralta_pl[MLXSW_REG_RALTA_LEN];
525
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200526 mlxsw_reg_ralta_pack(ralta_pl, false,
527 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
528 lpm_tree->id);
Ido Schimmelcc702672017-08-14 10:54:03 +0200529 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
Jiri Pirko53342022016-07-04 08:23:08 +0200530}
531
532static int
533mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
534 struct mlxsw_sp_prefix_usage *prefix_usage,
535 struct mlxsw_sp_lpm_tree *lpm_tree)
536{
537 char ralst_pl[MLXSW_REG_RALST_LEN];
538 u8 root_bin = 0;
539 u8 prefix;
540 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
541
542 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
543 root_bin = prefix;
544
545 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
546 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
547 if (prefix == 0)
548 continue;
549 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
550 MLXSW_REG_RALST_BIN_NO_CHILD);
551 last_prefix = prefix;
552 }
553 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
554}
555
556static struct mlxsw_sp_lpm_tree *
557mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
558 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100559 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200560{
561 struct mlxsw_sp_lpm_tree *lpm_tree;
562 int err;
563
Ido Schimmel382dbb42017-03-10 08:53:40 +0100564 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
Jiri Pirko53342022016-07-04 08:23:08 +0200565 if (!lpm_tree)
566 return ERR_PTR(-EBUSY);
567 lpm_tree->proto = proto;
568 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
569 if (err)
570 return ERR_PTR(err);
571
572 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
573 lpm_tree);
574 if (err)
575 goto err_left_struct_set;
Jiri Pirko2083d362016-10-25 11:25:56 +0200576 memcpy(&lpm_tree->prefix_usage, prefix_usage,
577 sizeof(lpm_tree->prefix_usage));
Jiri Pirko53342022016-07-04 08:23:08 +0200578 return lpm_tree;
579
580err_left_struct_set:
581 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
582 return ERR_PTR(err);
583}
584
Ido Schimmelcc702672017-08-14 10:54:03 +0200585static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
586 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200587{
Ido Schimmelcc702672017-08-14 10:54:03 +0200588 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200589}
590
591static struct mlxsw_sp_lpm_tree *
592mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
593 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100594 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200595{
596 struct mlxsw_sp_lpm_tree *lpm_tree;
597 int i;
598
Ido Schimmel9011b672017-05-16 19:38:25 +0200599 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
600 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko8b99bec2016-10-25 11:25:57 +0200601 if (lpm_tree->ref_count != 0 &&
602 lpm_tree->proto == proto &&
Jiri Pirko53342022016-07-04 08:23:08 +0200603 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
604 prefix_usage))
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200605 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200606 }
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200607 return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto);
608}
Jiri Pirko53342022016-07-04 08:23:08 +0200609
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200610static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree)
611{
Jiri Pirko53342022016-07-04 08:23:08 +0200612 lpm_tree->ref_count++;
Jiri Pirko53342022016-07-04 08:23:08 +0200613}
614
Ido Schimmelcc702672017-08-14 10:54:03 +0200615static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
616 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200617{
618 if (--lpm_tree->ref_count == 0)
Ido Schimmelcc702672017-08-14 10:54:03 +0200619 mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200620}
621
Ido Schimmeld7a60302017-06-08 08:47:43 +0200622#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
Ido Schimmel8494ab02017-03-24 08:02:47 +0100623
624static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200625{
626 struct mlxsw_sp_lpm_tree *lpm_tree;
Ido Schimmel8494ab02017-03-24 08:02:47 +0100627 u64 max_trees;
Jiri Pirko53342022016-07-04 08:23:08 +0200628 int i;
629
Ido Schimmel8494ab02017-03-24 08:02:47 +0100630 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
631 return -EIO;
632
633 max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
Ido Schimmel9011b672017-05-16 19:38:25 +0200634 mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
635 mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,
Ido Schimmel8494ab02017-03-24 08:02:47 +0100636 sizeof(struct mlxsw_sp_lpm_tree),
637 GFP_KERNEL);
Ido Schimmel9011b672017-05-16 19:38:25 +0200638 if (!mlxsw_sp->router->lpm.trees)
Ido Schimmel8494ab02017-03-24 08:02:47 +0100639 return -ENOMEM;
640
Ido Schimmel9011b672017-05-16 19:38:25 +0200641 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
642 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko53342022016-07-04 08:23:08 +0200643 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
644 }
Ido Schimmel8494ab02017-03-24 08:02:47 +0100645
646 return 0;
647}
648
649static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
650{
Ido Schimmel9011b672017-05-16 19:38:25 +0200651 kfree(mlxsw_sp->router->lpm.trees);
Jiri Pirko53342022016-07-04 08:23:08 +0200652}
653
Ido Schimmel76610eb2017-03-10 08:53:41 +0100654static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
655{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200656 return !!vr->fib4 || !!vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100657}
658
Jiri Pirko6b75c482016-07-04 08:23:09 +0200659static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
660{
661 struct mlxsw_sp_vr *vr;
662 int i;
663
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200664 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200665 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100666 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200667 return vr;
668 }
669 return NULL;
670}
671
672static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200673 const struct mlxsw_sp_fib *fib, u8 tree_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200674{
675 char raltb_pl[MLXSW_REG_RALTB_LEN];
676
Ido Schimmel76610eb2017-03-10 08:53:41 +0100677 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
678 (enum mlxsw_reg_ralxx_protocol) fib->proto,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200679 tree_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200680 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
681}
682
683static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100684 const struct mlxsw_sp_fib *fib)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200685{
686 char raltb_pl[MLXSW_REG_RALTB_LEN];
687
688 /* Bind to tree 0 which is default */
Ido Schimmel76610eb2017-03-10 08:53:41 +0100689 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
690 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200691 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
692}
693
694static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
695{
696 /* For our purpose, squash main and local table into one */
697 if (tb_id == RT_TABLE_LOCAL)
698 tb_id = RT_TABLE_MAIN;
699 return tb_id;
700}
701
702static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100703 u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200704{
705 struct mlxsw_sp_vr *vr;
706 int i;
707
708 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Nogah Frankel9497c042016-09-20 11:16:54 +0200709
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200710 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200711 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100712 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200713 return vr;
714 }
715 return NULL;
716}
717
Ido Schimmel76610eb2017-03-10 08:53:41 +0100718static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
719 enum mlxsw_sp_l3proto proto)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200720{
Ido Schimmel76610eb2017-03-10 08:53:41 +0100721 switch (proto) {
722 case MLXSW_SP_L3_PROTO_IPV4:
723 return vr->fib4;
724 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200725 return vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100726 }
727 return NULL;
728}
729
730static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
731 u32 tb_id)
732{
Jiri Pirko6b75c482016-07-04 08:23:09 +0200733 struct mlxsw_sp_vr *vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200734 int err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200735
736 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
737 if (!vr)
738 return ERR_PTR(-EBUSY);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100739 vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
740 if (IS_ERR(vr->fib4))
741 return ERR_CAST(vr->fib4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200742 vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6);
743 if (IS_ERR(vr->fib6)) {
744 err = PTR_ERR(vr->fib6);
745 goto err_fib6_create;
746 }
Jiri Pirko6b75c482016-07-04 08:23:09 +0200747 vr->tb_id = tb_id;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200748 return vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200749
750err_fib6_create:
751 mlxsw_sp_fib_destroy(vr->fib4);
752 vr->fib4 = NULL;
753 return ERR_PTR(err);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200754}
755
Ido Schimmel76610eb2017-03-10 08:53:41 +0100756static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200757{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200758 mlxsw_sp_fib_destroy(vr->fib6);
759 vr->fib6 = NULL;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100760 mlxsw_sp_fib_destroy(vr->fib4);
761 vr->fib4 = NULL;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200762}
763
Ido Schimmel76610eb2017-03-10 08:53:41 +0100764static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200765{
766 struct mlxsw_sp_vr *vr;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200767
768 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100769 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
770 if (!vr)
771 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200772 return vr;
773}
774
Ido Schimmel76610eb2017-03-10 08:53:41 +0100775static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200776{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200777 if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
778 list_empty(&vr->fib6->node_list))
Ido Schimmel76610eb2017-03-10 08:53:41 +0100779 mlxsw_sp_vr_destroy(vr);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200780}
781
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200782static bool
783mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr,
784 enum mlxsw_sp_l3proto proto, u8 tree_id)
785{
786 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
787
788 if (!mlxsw_sp_vr_is_used(vr))
789 return false;
790 if (fib->lpm_tree && fib->lpm_tree->id == tree_id)
791 return true;
792 return false;
793}
794
795static int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
796 struct mlxsw_sp_fib *fib,
797 struct mlxsw_sp_lpm_tree *new_tree)
798{
799 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
800 int err;
801
802 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
803 if (err)
804 return err;
805 fib->lpm_tree = new_tree;
806 mlxsw_sp_lpm_tree_hold(new_tree);
807 mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree);
808 return 0;
809}
810
811static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
812 struct mlxsw_sp_fib *fib,
813 struct mlxsw_sp_lpm_tree *new_tree)
814{
815 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
816 enum mlxsw_sp_l3proto proto = fib->proto;
817 u8 old_id, new_id = new_tree->id;
818 struct mlxsw_sp_vr *vr;
819 int i, err;
820
821 if (!old_tree)
822 goto no_replace;
823 old_id = old_tree->id;
824
825 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
826 vr = &mlxsw_sp->router->vrs[i];
827 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id))
828 continue;
829 err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
830 mlxsw_sp_vr_fib(vr, proto),
831 new_tree);
832 if (err)
833 goto err_tree_replace;
834 }
835
836 return 0;
837
838err_tree_replace:
839 for (i--; i >= 0; i--) {
840 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id))
841 continue;
842 mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
843 mlxsw_sp_vr_fib(vr, proto),
844 old_tree);
845 }
846 return err;
847
848no_replace:
849 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
850 if (err)
851 return err;
852 fib->lpm_tree = new_tree;
853 mlxsw_sp_lpm_tree_hold(new_tree);
854 return 0;
855}
856
857static void
858mlxsw_sp_vrs_prefixes(struct mlxsw_sp *mlxsw_sp,
859 enum mlxsw_sp_l3proto proto,
860 struct mlxsw_sp_prefix_usage *req_prefix_usage)
861{
862 int i;
863
864 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
865 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
866 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
867 unsigned char prefix;
868
869 if (!mlxsw_sp_vr_is_used(vr))
870 continue;
871 mlxsw_sp_prefix_usage_for_each(prefix, &fib->prefix_usage)
872 mlxsw_sp_prefix_usage_set(req_prefix_usage, prefix);
873 }
874}
875
Nogah Frankel9497c042016-09-20 11:16:54 +0200876static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200877{
878 struct mlxsw_sp_vr *vr;
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200879 u64 max_vrs;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200880 int i;
881
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200882 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
Nogah Frankel9497c042016-09-20 11:16:54 +0200883 return -EIO;
884
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200885 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
Ido Schimmel9011b672017-05-16 19:38:25 +0200886 mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
887 GFP_KERNEL);
888 if (!mlxsw_sp->router->vrs)
Nogah Frankel9497c042016-09-20 11:16:54 +0200889 return -ENOMEM;
890
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200891 for (i = 0; i < max_vrs; i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200892 vr = &mlxsw_sp->router->vrs[i];
Jiri Pirko6b75c482016-07-04 08:23:09 +0200893 vr->id = i;
894 }
Nogah Frankel9497c042016-09-20 11:16:54 +0200895
896 return 0;
897}
898
Ido Schimmelac571de2016-11-14 11:26:32 +0100899static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
900
Nogah Frankel9497c042016-09-20 11:16:54 +0200901static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
902{
Ido Schimmel30572242016-12-03 16:45:01 +0100903 /* At this stage we're guaranteed not to have new incoming
904 * FIB notifications and the work queue is free from FIBs
905 * sitting on top of mlxsw netdevs. However, we can still
906 * have other FIBs queued. Flush the queue before flushing
907 * the device's tables. No need for locks, as we're the only
908 * writer.
909 */
910 mlxsw_core_flush_owq();
Ido Schimmelac571de2016-11-14 11:26:32 +0100911 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +0200912 kfree(mlxsw_sp->router->vrs);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200913}
914
Petr Machata6ddb7422017-09-02 23:49:19 +0200915static struct net_device *
916__mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev)
917{
918 struct ip_tunnel *tun = netdev_priv(ol_dev);
919 struct net *net = dev_net(ol_dev);
920
921 return __dev_get_by_index(net, tun->parms.link);
922}
923
924static u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev)
925{
926 struct net_device *d = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);
927
928 if (d)
929 return l3mdev_fib_table(d) ? : RT_TABLE_MAIN;
930 else
931 return l3mdev_fib_table(ol_dev) ? : RT_TABLE_MAIN;
932}
933
Petr Machata1012b9a2017-09-02 23:49:23 +0200934static struct mlxsw_sp_rif *
935mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
936 const struct mlxsw_sp_rif_params *params);
937
938static struct mlxsw_sp_rif_ipip_lb *
939mlxsw_sp_ipip_ol_ipip_lb_create(struct mlxsw_sp *mlxsw_sp,
940 enum mlxsw_sp_ipip_type ipipt,
941 struct net_device *ol_dev)
942{
943 struct mlxsw_sp_rif_params_ipip_lb lb_params;
944 const struct mlxsw_sp_ipip_ops *ipip_ops;
945 struct mlxsw_sp_rif *rif;
946
947 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt];
948 lb_params = (struct mlxsw_sp_rif_params_ipip_lb) {
949 .common.dev = ol_dev,
950 .common.lag = false,
951 .lb_config = ipip_ops->ol_loopback_config(mlxsw_sp, ol_dev),
952 };
953
954 rif = mlxsw_sp_rif_create(mlxsw_sp, &lb_params.common);
955 if (IS_ERR(rif))
956 return ERR_CAST(rif);
957 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
958}
959
960static struct mlxsw_sp_ipip_entry *
961mlxsw_sp_ipip_entry_alloc(struct mlxsw_sp *mlxsw_sp,
962 enum mlxsw_sp_ipip_type ipipt,
963 struct net_device *ol_dev)
964{
965 struct mlxsw_sp_ipip_entry *ipip_entry;
966 struct mlxsw_sp_ipip_entry *ret = NULL;
967
968 ipip_entry = kzalloc(sizeof(*ipip_entry), GFP_KERNEL);
969 if (!ipip_entry)
970 return ERR_PTR(-ENOMEM);
971
972 ipip_entry->ol_lb = mlxsw_sp_ipip_ol_ipip_lb_create(mlxsw_sp, ipipt,
973 ol_dev);
974 if (IS_ERR(ipip_entry->ol_lb)) {
975 ret = ERR_CAST(ipip_entry->ol_lb);
976 goto err_ol_ipip_lb_create;
977 }
978
979 ipip_entry->ipipt = ipipt;
980 ipip_entry->ol_dev = ol_dev;
981
982 return ipip_entry;
983
984err_ol_ipip_lb_create:
985 kfree(ipip_entry);
986 return ret;
987}
988
989static void
990mlxsw_sp_ipip_entry_destroy(struct mlxsw_sp_ipip_entry *ipip_entry)
991{
992 WARN_ON(ipip_entry->ref_count > 0);
993 mlxsw_sp_rif_destroy(&ipip_entry->ol_lb->common);
994 kfree(ipip_entry);
995}
996
997static __be32
998mlxsw_sp_ipip_netdev_saddr4(const struct net_device *ol_dev)
999{
1000 struct ip_tunnel *tun = netdev_priv(ol_dev);
1001
1002 return tun->parms.iph.saddr;
1003}
1004
1005union mlxsw_sp_l3addr
1006mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto,
1007 const struct net_device *ol_dev)
1008{
1009 switch (proto) {
1010 case MLXSW_SP_L3_PROTO_IPV4:
1011 return (union mlxsw_sp_l3addr) {
1012 .addr4 = mlxsw_sp_ipip_netdev_saddr4(ol_dev),
1013 };
1014 case MLXSW_SP_L3_PROTO_IPV6:
1015 break;
1016 };
1017
1018 WARN_ON(1);
1019 return (union mlxsw_sp_l3addr) {
1020 .addr4 = 0,
1021 };
1022}
1023
Petr Machataee954d1a2017-09-02 23:49:29 +02001024__be32 mlxsw_sp_ipip_netdev_daddr4(const struct net_device *ol_dev)
1025{
1026 struct ip_tunnel *tun = netdev_priv(ol_dev);
1027
1028 return tun->parms.iph.daddr;
1029}
1030
1031union mlxsw_sp_l3addr
1032mlxsw_sp_ipip_netdev_daddr(enum mlxsw_sp_l3proto proto,
1033 const struct net_device *ol_dev)
1034{
1035 switch (proto) {
1036 case MLXSW_SP_L3_PROTO_IPV4:
1037 return (union mlxsw_sp_l3addr) {
1038 .addr4 = mlxsw_sp_ipip_netdev_daddr4(ol_dev),
1039 };
1040 case MLXSW_SP_L3_PROTO_IPV6:
1041 break;
1042 };
1043
1044 WARN_ON(1);
1045 return (union mlxsw_sp_l3addr) {
1046 .addr4 = 0,
1047 };
1048}
1049
Petr Machata1012b9a2017-09-02 23:49:23 +02001050static bool mlxsw_sp_l3addr_eq(const union mlxsw_sp_l3addr *addr1,
1051 const union mlxsw_sp_l3addr *addr2)
1052{
1053 return !memcmp(addr1, addr2, sizeof(*addr1));
1054}
1055
1056static bool
1057mlxsw_sp_ipip_entry_saddr_matches(struct mlxsw_sp *mlxsw_sp,
1058 const enum mlxsw_sp_l3proto ul_proto,
1059 union mlxsw_sp_l3addr saddr,
1060 u32 ul_tb_id,
1061 struct mlxsw_sp_ipip_entry *ipip_entry)
1062{
1063 u32 tun_ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
1064 enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
1065 union mlxsw_sp_l3addr tun_saddr;
1066
1067 if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
1068 return false;
1069
1070 tun_saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ipip_entry->ol_dev);
1071 return tun_ul_tb_id == ul_tb_id &&
1072 mlxsw_sp_l3addr_eq(&tun_saddr, &saddr);
1073}
1074
Petr Machata4607f6d2017-09-02 23:49:25 +02001075static int
1076mlxsw_sp_fib_entry_decap_init(struct mlxsw_sp *mlxsw_sp,
1077 struct mlxsw_sp_fib_entry *fib_entry,
1078 struct mlxsw_sp_ipip_entry *ipip_entry)
1079{
1080 u32 tunnel_index;
1081 int err;
1082
1083 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, 1, &tunnel_index);
1084 if (err)
1085 return err;
1086
1087 ipip_entry->decap_fib_entry = fib_entry;
1088 fib_entry->decap.ipip_entry = ipip_entry;
1089 fib_entry->decap.tunnel_index = tunnel_index;
1090 return 0;
1091}
1092
1093static void mlxsw_sp_fib_entry_decap_fini(struct mlxsw_sp *mlxsw_sp,
1094 struct mlxsw_sp_fib_entry *fib_entry)
1095{
1096 /* Unlink this node from the IPIP entry that it's the decap entry of. */
1097 fib_entry->decap.ipip_entry->decap_fib_entry = NULL;
1098 fib_entry->decap.ipip_entry = NULL;
1099 mlxsw_sp_kvdl_free(mlxsw_sp, fib_entry->decap.tunnel_index);
1100}
1101
Petr Machata1cc38fb2017-09-02 23:49:26 +02001102static struct mlxsw_sp_fib_node *
1103mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
1104 size_t addr_len, unsigned char prefix_len);
Petr Machata4607f6d2017-09-02 23:49:25 +02001105static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1106 struct mlxsw_sp_fib_entry *fib_entry);
1107
1108static void
1109mlxsw_sp_ipip_entry_demote_decap(struct mlxsw_sp *mlxsw_sp,
1110 struct mlxsw_sp_ipip_entry *ipip_entry)
1111{
1112 struct mlxsw_sp_fib_entry *fib_entry = ipip_entry->decap_fib_entry;
1113
1114 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, fib_entry);
1115 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
1116
1117 mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1118}
1119
Petr Machata1cc38fb2017-09-02 23:49:26 +02001120static void
1121mlxsw_sp_ipip_entry_promote_decap(struct mlxsw_sp *mlxsw_sp,
1122 struct mlxsw_sp_ipip_entry *ipip_entry,
1123 struct mlxsw_sp_fib_entry *decap_fib_entry)
1124{
1125 if (mlxsw_sp_fib_entry_decap_init(mlxsw_sp, decap_fib_entry,
1126 ipip_entry))
1127 return;
1128 decap_fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
1129
1130 if (mlxsw_sp_fib_entry_update(mlxsw_sp, decap_fib_entry))
1131 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
1132}
1133
1134/* Given an IPIP entry, find the corresponding decap route. */
1135static struct mlxsw_sp_fib_entry *
1136mlxsw_sp_ipip_entry_find_decap(struct mlxsw_sp *mlxsw_sp,
1137 struct mlxsw_sp_ipip_entry *ipip_entry)
1138{
1139 static struct mlxsw_sp_fib_node *fib_node;
1140 const struct mlxsw_sp_ipip_ops *ipip_ops;
1141 struct mlxsw_sp_fib_entry *fib_entry;
1142 unsigned char saddr_prefix_len;
1143 union mlxsw_sp_l3addr saddr;
1144 struct mlxsw_sp_fib *ul_fib;
1145 struct mlxsw_sp_vr *ul_vr;
1146 const void *saddrp;
1147 size_t saddr_len;
1148 u32 ul_tb_id;
1149 u32 saddr4;
1150
1151 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
1152
1153 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
1154 ul_vr = mlxsw_sp_vr_find(mlxsw_sp, ul_tb_id);
1155 if (!ul_vr)
1156 return NULL;
1157
1158 ul_fib = mlxsw_sp_vr_fib(ul_vr, ipip_ops->ul_proto);
1159 saddr = mlxsw_sp_ipip_netdev_saddr(ipip_ops->ul_proto,
1160 ipip_entry->ol_dev);
1161
1162 switch (ipip_ops->ul_proto) {
1163 case MLXSW_SP_L3_PROTO_IPV4:
1164 saddr4 = be32_to_cpu(saddr.addr4);
1165 saddrp = &saddr4;
1166 saddr_len = 4;
1167 saddr_prefix_len = 32;
1168 break;
1169 case MLXSW_SP_L3_PROTO_IPV6:
1170 WARN_ON(1);
1171 return NULL;
1172 }
1173
1174 fib_node = mlxsw_sp_fib_node_lookup(ul_fib, saddrp, saddr_len,
1175 saddr_prefix_len);
1176 if (!fib_node || list_empty(&fib_node->entry_list))
1177 return NULL;
1178
1179 fib_entry = list_first_entry(&fib_node->entry_list,
1180 struct mlxsw_sp_fib_entry, list);
1181 if (fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP)
1182 return NULL;
1183
1184 return fib_entry;
1185}
1186
Petr Machata1012b9a2017-09-02 23:49:23 +02001187static struct mlxsw_sp_ipip_entry *
1188mlxsw_sp_ipip_entry_get(struct mlxsw_sp *mlxsw_sp,
1189 enum mlxsw_sp_ipip_type ipipt,
1190 struct net_device *ol_dev)
1191{
1192 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev);
1193 struct mlxsw_sp_router *router = mlxsw_sp->router;
Petr Machata1cc38fb2017-09-02 23:49:26 +02001194 struct mlxsw_sp_fib_entry *decap_fib_entry;
Petr Machata1012b9a2017-09-02 23:49:23 +02001195 struct mlxsw_sp_ipip_entry *ipip_entry;
1196 enum mlxsw_sp_l3proto ul_proto;
1197 union mlxsw_sp_l3addr saddr;
1198
1199 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
1200 ipip_list_node) {
1201 if (ipip_entry->ol_dev == ol_dev)
1202 goto inc_ref_count;
1203
1204 /* The configuration where several tunnels have the same local
1205 * address in the same underlay table needs special treatment in
1206 * the HW. That is currently not implemented in the driver.
1207 */
1208 ul_proto = router->ipip_ops_arr[ipip_entry->ipipt]->ul_proto;
1209 saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ol_dev);
1210 if (mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, saddr,
1211 ul_tb_id, ipip_entry))
1212 return ERR_PTR(-EEXIST);
1213 }
1214
1215 ipip_entry = mlxsw_sp_ipip_entry_alloc(mlxsw_sp, ipipt, ol_dev);
1216 if (IS_ERR(ipip_entry))
1217 return ipip_entry;
1218
Petr Machata1cc38fb2017-09-02 23:49:26 +02001219 decap_fib_entry = mlxsw_sp_ipip_entry_find_decap(mlxsw_sp, ipip_entry);
1220 if (decap_fib_entry)
1221 mlxsw_sp_ipip_entry_promote_decap(mlxsw_sp, ipip_entry,
1222 decap_fib_entry);
1223
Petr Machata1012b9a2017-09-02 23:49:23 +02001224 list_add_tail(&ipip_entry->ipip_list_node,
1225 &mlxsw_sp->router->ipip_list);
1226
1227inc_ref_count:
1228 ++ipip_entry->ref_count;
1229 return ipip_entry;
1230}
1231
1232static void
1233mlxsw_sp_ipip_entry_put(struct mlxsw_sp *mlxsw_sp,
1234 struct mlxsw_sp_ipip_entry *ipip_entry)
1235{
1236 if (--ipip_entry->ref_count == 0) {
1237 list_del(&ipip_entry->ipip_list_node);
Petr Machata4607f6d2017-09-02 23:49:25 +02001238 if (ipip_entry->decap_fib_entry)
1239 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
Petr Machata1012b9a2017-09-02 23:49:23 +02001240 mlxsw_sp_ipip_entry_destroy(ipip_entry);
1241 }
1242}
1243
Petr Machata4607f6d2017-09-02 23:49:25 +02001244static bool
1245mlxsw_sp_ipip_entry_matches_decap(struct mlxsw_sp *mlxsw_sp,
1246 const struct net_device *ul_dev,
1247 enum mlxsw_sp_l3proto ul_proto,
1248 union mlxsw_sp_l3addr ul_dip,
1249 struct mlxsw_sp_ipip_entry *ipip_entry)
1250{
1251 u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN;
1252 enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
1253 struct net_device *ipip_ul_dev;
1254
1255 if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
1256 return false;
1257
1258 ipip_ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ipip_entry->ol_dev);
1259 return mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, ul_dip,
1260 ul_tb_id, ipip_entry) &&
1261 (!ipip_ul_dev || ipip_ul_dev == ul_dev);
1262}
1263
1264/* Given decap parameters, find the corresponding IPIP entry. */
1265static struct mlxsw_sp_ipip_entry *
1266mlxsw_sp_ipip_entry_find_by_decap(struct mlxsw_sp *mlxsw_sp,
1267 const struct net_device *ul_dev,
1268 enum mlxsw_sp_l3proto ul_proto,
1269 union mlxsw_sp_l3addr ul_dip)
1270{
1271 struct mlxsw_sp_ipip_entry *ipip_entry;
1272
1273 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
1274 ipip_list_node)
1275 if (mlxsw_sp_ipip_entry_matches_decap(mlxsw_sp, ul_dev,
1276 ul_proto, ul_dip,
1277 ipip_entry))
1278 return ipip_entry;
1279
1280 return NULL;
1281}
1282
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001283struct mlxsw_sp_neigh_key {
Jiri Pirko33b13412016-11-10 12:31:04 +01001284 struct neighbour *n;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001285};
1286
1287struct mlxsw_sp_neigh_entry {
Ido Schimmel9665b742017-02-08 11:16:42 +01001288 struct list_head rif_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001289 struct rhash_head ht_node;
1290 struct mlxsw_sp_neigh_key key;
1291 u16 rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001292 bool connected;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001293 unsigned char ha[ETH_ALEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001294 struct list_head nexthop_list; /* list of nexthops using
1295 * this neigh entry
1296 */
Yotam Gigib2157142016-07-05 11:27:51 +02001297 struct list_head nexthop_neighs_list_node;
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001298 unsigned int counter_index;
1299 bool counter_valid;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001300};
1301
1302static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
1303 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
1304 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
1305 .key_len = sizeof(struct mlxsw_sp_neigh_key),
1306};
1307
Arkadi Sharshevskyf17cc842017-08-24 08:40:04 +02001308struct mlxsw_sp_neigh_entry *
1309mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif,
1310 struct mlxsw_sp_neigh_entry *neigh_entry)
1311{
1312 if (!neigh_entry) {
1313 if (list_empty(&rif->neigh_list))
1314 return NULL;
1315 else
1316 return list_first_entry(&rif->neigh_list,
1317 typeof(*neigh_entry),
1318 rif_list_node);
1319 }
Arkadi Sharshevskyec2437f2017-09-25 10:32:24 +02001320 if (list_is_last(&neigh_entry->rif_list_node, &rif->neigh_list))
Arkadi Sharshevskyf17cc842017-08-24 08:40:04 +02001321 return NULL;
1322 return list_next_entry(neigh_entry, rif_list_node);
1323}
1324
1325int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry)
1326{
1327 return neigh_entry->key.n->tbl->family;
1328}
1329
1330unsigned char *
1331mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry)
1332{
1333 return neigh_entry->ha;
1334}
1335
1336u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
1337{
1338 struct neighbour *n;
1339
1340 n = neigh_entry->key.n;
1341 return ntohl(*((__be32 *) n->primary_key));
1342}
1343
Arkadi Sharshevsky02507682017-08-31 17:59:15 +02001344struct in6_addr *
1345mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
1346{
1347 struct neighbour *n;
1348
1349 n = neigh_entry->key.n;
1350 return (struct in6_addr *) &n->primary_key;
1351}
1352
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001353int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp,
1354 struct mlxsw_sp_neigh_entry *neigh_entry,
1355 u64 *p_counter)
1356{
1357 if (!neigh_entry->counter_valid)
1358 return -EINVAL;
1359
1360 return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index,
1361 p_counter, NULL);
1362}
1363
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001364static struct mlxsw_sp_neigh_entry *
1365mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
1366 u16 rif)
1367{
1368 struct mlxsw_sp_neigh_entry *neigh_entry;
1369
1370 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
1371 if (!neigh_entry)
1372 return NULL;
1373
1374 neigh_entry->key.n = n;
1375 neigh_entry->rif = rif;
1376 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
1377
1378 return neigh_entry;
1379}
1380
1381static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
1382{
1383 kfree(neigh_entry);
1384}
1385
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001386static int
1387mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
1388 struct mlxsw_sp_neigh_entry *neigh_entry)
1389{
Ido Schimmel9011b672017-05-16 19:38:25 +02001390 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001391 &neigh_entry->ht_node,
1392 mlxsw_sp_neigh_ht_params);
1393}
1394
1395static void
1396mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
1397 struct mlxsw_sp_neigh_entry *neigh_entry)
1398{
Ido Schimmel9011b672017-05-16 19:38:25 +02001399 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001400 &neigh_entry->ht_node,
1401 mlxsw_sp_neigh_ht_params);
1402}
1403
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001404static bool
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001405mlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp,
1406 struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001407{
1408 struct devlink *devlink;
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001409 const char *table_name;
1410
1411 switch (mlxsw_sp_neigh_entry_type(neigh_entry)) {
1412 case AF_INET:
1413 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4;
1414 break;
1415 case AF_INET6:
1416 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6;
1417 break;
1418 default:
1419 WARN_ON(1);
1420 return false;
1421 }
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001422
1423 devlink = priv_to_devlink(mlxsw_sp->core);
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001424 return devlink_dpipe_table_counter_enabled(devlink, table_name);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001425}
1426
1427static void
1428mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp,
1429 struct mlxsw_sp_neigh_entry *neigh_entry)
1430{
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001431 if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry))
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001432 return;
1433
1434 if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index))
1435 return;
1436
1437 neigh_entry->counter_valid = true;
1438}
1439
1440static void
1441mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp,
1442 struct mlxsw_sp_neigh_entry *neigh_entry)
1443{
1444 if (!neigh_entry->counter_valid)
1445 return;
1446 mlxsw_sp_flow_counter_free(mlxsw_sp,
1447 neigh_entry->counter_index);
1448 neigh_entry->counter_valid = false;
1449}
1450
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001451static struct mlxsw_sp_neigh_entry *
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001452mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001453{
1454 struct mlxsw_sp_neigh_entry *neigh_entry;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001455 struct mlxsw_sp_rif *rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001456 int err;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001457
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001458 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
1459 if (!rif)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001460 return ERR_PTR(-EINVAL);
1461
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001462 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001463 if (!neigh_entry)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001464 return ERR_PTR(-ENOMEM);
1465
1466 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
1467 if (err)
1468 goto err_neigh_entry_insert;
1469
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001470 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001471 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001472
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001473 return neigh_entry;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001474
1475err_neigh_entry_insert:
1476 mlxsw_sp_neigh_entry_free(neigh_entry);
1477 return ERR_PTR(err);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001478}
1479
1480static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001481mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
1482 struct mlxsw_sp_neigh_entry *neigh_entry)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001483{
Ido Schimmel9665b742017-02-08 11:16:42 +01001484 list_del(&neigh_entry->rif_list_node);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001485 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001486 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
1487 mlxsw_sp_neigh_entry_free(neigh_entry);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001488}
1489
1490static struct mlxsw_sp_neigh_entry *
Jiri Pirko33b13412016-11-10 12:31:04 +01001491mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001492{
Jiri Pirko33b13412016-11-10 12:31:04 +01001493 struct mlxsw_sp_neigh_key key;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001494
Jiri Pirko33b13412016-11-10 12:31:04 +01001495 key.n = n;
Ido Schimmel9011b672017-05-16 19:38:25 +02001496 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001497 &key, mlxsw_sp_neigh_ht_params);
1498}
1499
Yotam Gigic723c7352016-07-05 11:27:43 +02001500static void
1501mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
1502{
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001503 unsigned long interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001504
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001505#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001506 interval = min_t(unsigned long,
1507 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
1508 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001509#else
1510 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
1511#endif
Ido Schimmel9011b672017-05-16 19:38:25 +02001512 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
Yotam Gigic723c7352016-07-05 11:27:43 +02001513}
1514
1515static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1516 char *rauhtd_pl,
1517 int ent_index)
1518{
1519 struct net_device *dev;
1520 struct neighbour *n;
1521 __be32 dipn;
1522 u32 dip;
1523 u16 rif;
1524
1525 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
1526
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001527 if (!mlxsw_sp->router->rifs[rif]) {
Yotam Gigic723c7352016-07-05 11:27:43 +02001528 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1529 return;
1530 }
1531
1532 dipn = htonl(dip);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001533 dev = mlxsw_sp->router->rifs[rif]->dev;
Yotam Gigic723c7352016-07-05 11:27:43 +02001534 n = neigh_lookup(&arp_tbl, &dipn, dev);
1535 if (!n) {
1536 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
1537 &dip);
1538 return;
1539 }
1540
1541 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
1542 neigh_event_send(n, NULL);
1543 neigh_release(n);
1544}
1545
Ido Schimmeldf9a21f2017-08-15 09:10:33 +02001546#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001547static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1548 char *rauhtd_pl,
1549 int rec_index)
1550{
1551 struct net_device *dev;
1552 struct neighbour *n;
1553 struct in6_addr dip;
1554 u16 rif;
1555
1556 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1557 (char *) &dip);
1558
1559 if (!mlxsw_sp->router->rifs[rif]) {
1560 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1561 return;
1562 }
1563
1564 dev = mlxsw_sp->router->rifs[rif]->dev;
1565 n = neigh_lookup(&nd_tbl, &dip, dev);
1566 if (!n) {
1567 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1568 &dip);
1569 return;
1570 }
1571
1572 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1573 neigh_event_send(n, NULL);
1574 neigh_release(n);
1575}
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001576#else
1577static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1578 char *rauhtd_pl,
1579 int rec_index)
1580{
1581}
1582#endif
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001583
Yotam Gigic723c7352016-07-05 11:27:43 +02001584static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1585 char *rauhtd_pl,
1586 int rec_index)
1587{
1588 u8 num_entries;
1589 int i;
1590
1591 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1592 rec_index);
1593 /* Hardware starts counting at 0, so add 1. */
1594 num_entries++;
1595
1596 /* Each record consists of several neighbour entries. */
1597 for (i = 0; i < num_entries; i++) {
1598 int ent_index;
1599
1600 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1601 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1602 ent_index);
1603 }
1604
1605}
1606
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001607static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1608 char *rauhtd_pl,
1609 int rec_index)
1610{
1611 /* One record contains one entry. */
1612 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1613 rec_index);
1614}
1615
Yotam Gigic723c7352016-07-05 11:27:43 +02001616static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1617 char *rauhtd_pl, int rec_index)
1618{
1619 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1620 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1621 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1622 rec_index);
1623 break;
1624 case MLXSW_REG_RAUHTD_TYPE_IPV6:
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001625 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1626 rec_index);
Yotam Gigic723c7352016-07-05 11:27:43 +02001627 break;
1628 }
1629}
1630
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001631static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1632{
1633 u8 num_rec, last_rec_index, num_entries;
1634
1635 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1636 last_rec_index = num_rec - 1;
1637
1638 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1639 return false;
1640 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1641 MLXSW_REG_RAUHTD_TYPE_IPV6)
1642 return true;
1643
1644 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1645 last_rec_index);
1646 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1647 return true;
1648 return false;
1649}
1650
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001651static int
1652__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1653 char *rauhtd_pl,
1654 enum mlxsw_reg_rauhtd_type type)
Yotam Gigic723c7352016-07-05 11:27:43 +02001655{
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001656 int i, num_rec;
1657 int err;
Yotam Gigic723c7352016-07-05 11:27:43 +02001658
1659 /* Make sure the neighbour's netdev isn't removed in the
1660 * process.
1661 */
1662 rtnl_lock();
1663 do {
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001664 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
Yotam Gigic723c7352016-07-05 11:27:43 +02001665 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1666 rauhtd_pl);
1667 if (err) {
1668 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
1669 break;
1670 }
1671 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1672 for (i = 0; i < num_rec; i++)
1673 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1674 i);
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001675 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
Yotam Gigic723c7352016-07-05 11:27:43 +02001676 rtnl_unlock();
1677
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001678 return err;
1679}
1680
1681static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1682{
1683 enum mlxsw_reg_rauhtd_type type;
1684 char *rauhtd_pl;
1685 int err;
1686
1687 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1688 if (!rauhtd_pl)
1689 return -ENOMEM;
1690
1691 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1692 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1693 if (err)
1694 goto out;
1695
1696 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1697 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1698out:
Yotam Gigic723c7352016-07-05 11:27:43 +02001699 kfree(rauhtd_pl);
Yotam Gigib2157142016-07-05 11:27:51 +02001700 return err;
1701}
1702
1703static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1704{
1705 struct mlxsw_sp_neigh_entry *neigh_entry;
1706
1707 /* Take RTNL mutex here to prevent lists from changes */
1708 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001709 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001710 nexthop_neighs_list_node)
Yotam Gigib2157142016-07-05 11:27:51 +02001711 /* If this neigh have nexthops, make the kernel think this neigh
1712 * is active regardless of the traffic.
1713 */
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001714 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigib2157142016-07-05 11:27:51 +02001715 rtnl_unlock();
1716}
1717
1718static void
1719mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1720{
Ido Schimmel9011b672017-05-16 19:38:25 +02001721 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
Yotam Gigib2157142016-07-05 11:27:51 +02001722
Ido Schimmel9011b672017-05-16 19:38:25 +02001723 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigib2157142016-07-05 11:27:51 +02001724 msecs_to_jiffies(interval));
1725}
1726
1727static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1728{
Ido Schimmel9011b672017-05-16 19:38:25 +02001729 struct mlxsw_sp_router *router;
Yotam Gigib2157142016-07-05 11:27:51 +02001730 int err;
1731
Ido Schimmel9011b672017-05-16 19:38:25 +02001732 router = container_of(work, struct mlxsw_sp_router,
1733 neighs_update.dw.work);
1734 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001735 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02001736 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
Yotam Gigib2157142016-07-05 11:27:51 +02001737
Ido Schimmel9011b672017-05-16 19:38:25 +02001738 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001739
Ido Schimmel9011b672017-05-16 19:38:25 +02001740 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
Yotam Gigic723c7352016-07-05 11:27:43 +02001741}
1742
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001743static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1744{
1745 struct mlxsw_sp_neigh_entry *neigh_entry;
Ido Schimmel9011b672017-05-16 19:38:25 +02001746 struct mlxsw_sp_router *router;
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001747
Ido Schimmel9011b672017-05-16 19:38:25 +02001748 router = container_of(work, struct mlxsw_sp_router,
1749 nexthop_probe_dw.work);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001750 /* Iterate over nexthop neighbours, find those who are unresolved and
1751 * send arp on them. This solves the chicken-egg problem when
1752 * the nexthop wouldn't get offloaded until the neighbor is resolved
1753 * but it wouldn't get resolved ever in case traffic is flowing in HW
1754 * using different nexthop.
1755 *
1756 * Take RTNL mutex here to prevent lists from changes.
1757 */
1758 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001759 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001760 nexthop_neighs_list_node)
Ido Schimmel01b1aa32017-02-06 16:20:16 +01001761 if (!neigh_entry->connected)
Jiri Pirko33b13412016-11-10 12:31:04 +01001762 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001763 rtnl_unlock();
1764
Ido Schimmel9011b672017-05-16 19:38:25 +02001765 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001766 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1767}
1768
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001769static void
1770mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1771 struct mlxsw_sp_neigh_entry *neigh_entry,
1772 bool removing);
1773
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001774static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001775{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001776 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1777 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1778}
1779
1780static void
1781mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1782 struct mlxsw_sp_neigh_entry *neigh_entry,
1783 enum mlxsw_reg_rauht_op op)
1784{
Jiri Pirko33b13412016-11-10 12:31:04 +01001785 struct neighbour *n = neigh_entry->key.n;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001786 u32 dip = ntohl(*((__be32 *) n->primary_key));
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001787 char rauht_pl[MLXSW_REG_RAUHT_LEN];
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001788
1789 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1790 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001791 if (neigh_entry->counter_valid)
1792 mlxsw_reg_rauht_pack_counter(rauht_pl,
1793 neigh_entry->counter_index);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001794 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1795}
1796
1797static void
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001798mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1799 struct mlxsw_sp_neigh_entry *neigh_entry,
1800 enum mlxsw_reg_rauht_op op)
1801{
1802 struct neighbour *n = neigh_entry->key.n;
1803 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1804 const char *dip = n->primary_key;
1805
1806 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1807 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001808 if (neigh_entry->counter_valid)
1809 mlxsw_reg_rauht_pack_counter(rauht_pl,
1810 neigh_entry->counter_index);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001811 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1812}
1813
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001814bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001815{
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001816 struct neighbour *n = neigh_entry->key.n;
1817
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001818 /* Packets with a link-local destination address are trapped
1819 * after LPM lookup and never reach the neighbour table, so
1820 * there is no need to program such neighbours to the device.
1821 */
1822 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1823 IPV6_ADDR_LINKLOCAL)
1824 return true;
1825 return false;
1826}
1827
1828static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001829mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1830 struct mlxsw_sp_neigh_entry *neigh_entry,
1831 bool adding)
1832{
1833 if (!adding && !neigh_entry->connected)
1834 return;
1835 neigh_entry->connected = adding;
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001836 if (neigh_entry->key.n->tbl->family == AF_INET) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001837 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1838 mlxsw_sp_rauht_op(adding));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001839 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001840 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry))
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001841 return;
1842 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1843 mlxsw_sp_rauht_op(adding));
1844 } else {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001845 WARN_ON_ONCE(1);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001846 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001847}
1848
Arkadi Sharshevskya481d712017-08-24 08:40:10 +02001849void
1850mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp,
1851 struct mlxsw_sp_neigh_entry *neigh_entry,
1852 bool adding)
1853{
1854 if (adding)
1855 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
1856 else
1857 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
1858 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true);
1859}
1860
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001861struct mlxsw_sp_neigh_event_work {
1862 struct work_struct work;
1863 struct mlxsw_sp *mlxsw_sp;
1864 struct neighbour *n;
1865};
1866
1867static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1868{
1869 struct mlxsw_sp_neigh_event_work *neigh_work =
1870 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1871 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1872 struct mlxsw_sp_neigh_entry *neigh_entry;
1873 struct neighbour *n = neigh_work->n;
1874 unsigned char ha[ETH_ALEN];
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001875 bool entry_connected;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001876 u8 nud_state, dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001877
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001878 /* If these parameters are changed after we release the lock,
1879 * then we are guaranteed to receive another event letting us
1880 * know about it.
1881 */
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001882 read_lock_bh(&n->lock);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001883 memcpy(ha, n->ha, ETH_ALEN);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001884 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001885 dead = n->dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001886 read_unlock_bh(&n->lock);
1887
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001888 rtnl_lock();
Ido Schimmel93a87e52016-12-23 09:32:49 +01001889 entry_connected = nud_state & NUD_VALID && !dead;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001890 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1891 if (!entry_connected && !neigh_entry)
1892 goto out;
1893 if (!neigh_entry) {
1894 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1895 if (IS_ERR(neigh_entry))
1896 goto out;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001897 }
1898
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001899 memcpy(neigh_entry->ha, ha, ETH_ALEN);
1900 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
1901 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
1902
1903 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1904 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1905
1906out:
1907 rtnl_unlock();
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001908 neigh_release(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001909 kfree(neigh_work);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001910}
1911
Jiri Pirkoe7322632016-09-01 10:37:43 +02001912int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
1913 unsigned long event, void *ptr)
Yotam Gigic723c7352016-07-05 11:27:43 +02001914{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001915 struct mlxsw_sp_neigh_event_work *neigh_work;
Yotam Gigic723c7352016-07-05 11:27:43 +02001916 struct mlxsw_sp_port *mlxsw_sp_port;
1917 struct mlxsw_sp *mlxsw_sp;
1918 unsigned long interval;
1919 struct neigh_parms *p;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001920 struct neighbour *n;
Yotam Gigic723c7352016-07-05 11:27:43 +02001921
1922 switch (event) {
1923 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
1924 p = ptr;
1925
1926 /* We don't care about changes in the default table. */
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001927 if (!p->dev || (p->tbl->family != AF_INET &&
1928 p->tbl->family != AF_INET6))
Yotam Gigic723c7352016-07-05 11:27:43 +02001929 return NOTIFY_DONE;
1930
1931 /* We are in atomic context and can't take RTNL mutex,
1932 * so use RCU variant to walk the device chain.
1933 */
1934 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
1935 if (!mlxsw_sp_port)
1936 return NOTIFY_DONE;
1937
1938 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1939 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
Ido Schimmel9011b672017-05-16 19:38:25 +02001940 mlxsw_sp->router->neighs_update.interval = interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001941
1942 mlxsw_sp_port_dev_put(mlxsw_sp_port);
1943 break;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001944 case NETEVENT_NEIGH_UPDATE:
1945 n = ptr;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001946
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001947 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001948 return NOTIFY_DONE;
1949
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001950 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001951 if (!mlxsw_sp_port)
1952 return NOTIFY_DONE;
1953
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001954 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
1955 if (!neigh_work) {
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001956 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001957 return NOTIFY_BAD;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001958 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001959
1960 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1961 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1962 neigh_work->n = n;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001963
1964 /* Take a reference to ensure the neighbour won't be
1965 * destructed until we drop the reference in delayed
1966 * work.
1967 */
1968 neigh_clone(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001969 mlxsw_core_schedule_work(&neigh_work->work);
1970 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001971 break;
Yotam Gigic723c7352016-07-05 11:27:43 +02001972 }
1973
1974 return NOTIFY_DONE;
1975}
1976
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001977static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1978{
Yotam Gigic723c7352016-07-05 11:27:43 +02001979 int err;
1980
Ido Schimmel9011b672017-05-16 19:38:25 +02001981 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
Yotam Gigic723c7352016-07-05 11:27:43 +02001982 &mlxsw_sp_neigh_ht_params);
1983 if (err)
1984 return err;
1985
1986 /* Initialize the polling interval according to the default
1987 * table.
1988 */
1989 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1990
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001991 /* Create the delayed works for the activity_update */
Ido Schimmel9011b672017-05-16 19:38:25 +02001992 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigic723c7352016-07-05 11:27:43 +02001993 mlxsw_sp_router_neighs_update_work);
Ido Schimmel9011b672017-05-16 19:38:25 +02001994 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001995 mlxsw_sp_router_probe_unresolved_nexthops);
Ido Schimmel9011b672017-05-16 19:38:25 +02001996 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
1997 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
Yotam Gigic723c7352016-07-05 11:27:43 +02001998 return 0;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001999}
2000
2001static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
2002{
Ido Schimmel9011b672017-05-16 19:38:25 +02002003 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
2004 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
2005 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02002006}
2007
Ido Schimmel9665b742017-02-08 11:16:42 +01002008static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002009 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002010{
2011 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
2012
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002013 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02002014 rif_list_node) {
2015 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
Ido Schimmel9665b742017-02-08 11:16:42 +01002016 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02002017 }
Ido Schimmel9665b742017-02-08 11:16:42 +01002018}
2019
Petr Machata35225e42017-09-02 23:49:22 +02002020enum mlxsw_sp_nexthop_type {
2021 MLXSW_SP_NEXTHOP_TYPE_ETH,
Petr Machata1012b9a2017-09-02 23:49:23 +02002022 MLXSW_SP_NEXTHOP_TYPE_IPIP,
Petr Machata35225e42017-09-02 23:49:22 +02002023};
2024
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002025struct mlxsw_sp_nexthop_key {
2026 struct fib_nh *fib_nh;
2027};
2028
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002029struct mlxsw_sp_nexthop {
2030 struct list_head neigh_list_node; /* member of neigh entry list */
Ido Schimmel9665b742017-02-08 11:16:42 +01002031 struct list_head rif_list_node;
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02002032 struct list_head router_list_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002033 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
2034 * this belongs to
2035 */
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002036 struct rhash_head ht_node;
2037 struct mlxsw_sp_nexthop_key key;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002038 unsigned char gw_addr[sizeof(struct in6_addr)];
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002039 int ifindex;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002040 struct mlxsw_sp_rif *rif;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002041 u8 should_offload:1, /* set indicates this neigh is connected and
2042 * should be put to KVD linear area of this group.
2043 */
2044 offloaded:1, /* set in case the neigh is actually put into
2045 * KVD linear area of this group.
2046 */
2047 update:1; /* set indicates that MAC of this neigh should be
2048 * updated in HW
2049 */
Petr Machata35225e42017-09-02 23:49:22 +02002050 enum mlxsw_sp_nexthop_type type;
2051 union {
2052 struct mlxsw_sp_neigh_entry *neigh_entry;
Petr Machata1012b9a2017-09-02 23:49:23 +02002053 struct mlxsw_sp_ipip_entry *ipip_entry;
Petr Machata35225e42017-09-02 23:49:22 +02002054 };
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002055};
2056
2057struct mlxsw_sp_nexthop_group {
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002058 void *priv;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002059 struct rhash_head ht_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002060 struct list_head fib_list; /* list of fib entries that use this group */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002061 struct neigh_table *neigh_tbl;
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002062 u8 adj_index_valid:1,
2063 gateway:1; /* routes using the group use a gateway */
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002064 u32 adj_index;
2065 u16 ecmp_size;
2066 u16 count;
2067 struct mlxsw_sp_nexthop nexthops[0];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002068#define nh_rif nexthops[0].rif
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002069};
2070
Arkadi Sharshevskyc556cd22017-09-25 10:32:25 +02002071struct mlxsw_sp_nexthop *mlxsw_sp_nexthop_next(struct mlxsw_sp_router *router,
2072 struct mlxsw_sp_nexthop *nh)
2073{
2074 if (!nh) {
2075 if (list_empty(&router->nexthop_list))
2076 return NULL;
2077 else
2078 return list_first_entry(&router->nexthop_list,
2079 typeof(*nh), router_list_node);
2080 }
2081 if (list_is_last(&nh->router_list_node, &router->nexthop_list))
2082 return NULL;
2083 return list_next_entry(nh, router_list_node);
2084}
2085
2086bool mlxsw_sp_nexthop_offload(struct mlxsw_sp_nexthop *nh)
2087{
2088 return nh->offloaded;
2089}
2090
2091unsigned char *mlxsw_sp_nexthop_ha(struct mlxsw_sp_nexthop *nh)
2092{
2093 if (!nh->offloaded)
2094 return NULL;
2095 return nh->neigh_entry->ha;
2096}
2097
2098int mlxsw_sp_nexthop_indexes(struct mlxsw_sp_nexthop *nh, u32 *p_adj_index,
2099 u32 *p_adj_hash_index)
2100{
2101 struct mlxsw_sp_nexthop_group *nh_grp = nh->nh_grp;
2102 u32 adj_hash_index = 0;
2103 int i;
2104
2105 if (!nh->offloaded || !nh_grp->adj_index_valid)
2106 return -EINVAL;
2107
2108 *p_adj_index = nh_grp->adj_index;
2109
2110 for (i = 0; i < nh_grp->count; i++) {
2111 struct mlxsw_sp_nexthop *nh_iter = &nh_grp->nexthops[i];
2112
2113 if (nh_iter == nh)
2114 break;
2115 if (nh_iter->offloaded)
2116 adj_hash_index++;
2117 }
2118
2119 *p_adj_hash_index = adj_hash_index;
2120 return 0;
2121}
2122
2123struct mlxsw_sp_rif *mlxsw_sp_nexthop_rif(struct mlxsw_sp_nexthop *nh)
2124{
2125 return nh->rif;
2126}
2127
2128bool mlxsw_sp_nexthop_group_has_ipip(struct mlxsw_sp_nexthop *nh)
2129{
2130 struct mlxsw_sp_nexthop_group *nh_grp = nh->nh_grp;
2131 int i;
2132
2133 for (i = 0; i < nh_grp->count; i++) {
2134 struct mlxsw_sp_nexthop *nh_iter = &nh_grp->nexthops[i];
2135
2136 if (nh_iter->type == MLXSW_SP_NEXTHOP_TYPE_IPIP)
2137 return true;
2138 }
2139 return false;
2140}
2141
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002142static struct fib_info *
2143mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
2144{
2145 return nh_grp->priv;
2146}
2147
2148struct mlxsw_sp_nexthop_group_cmp_arg {
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002149 enum mlxsw_sp_l3proto proto;
2150 union {
2151 struct fib_info *fi;
2152 struct mlxsw_sp_fib6_entry *fib6_entry;
2153 };
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002154};
2155
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002156static bool
2157mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,
2158 const struct in6_addr *gw, int ifindex)
2159{
2160 int i;
2161
2162 for (i = 0; i < nh_grp->count; i++) {
2163 const struct mlxsw_sp_nexthop *nh;
2164
2165 nh = &nh_grp->nexthops[i];
2166 if (nh->ifindex == ifindex &&
2167 ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))
2168 return true;
2169 }
2170
2171 return false;
2172}
2173
2174static bool
2175mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
2176 const struct mlxsw_sp_fib6_entry *fib6_entry)
2177{
2178 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2179
2180 if (nh_grp->count != fib6_entry->nrt6)
2181 return false;
2182
2183 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2184 struct in6_addr *gw;
2185 int ifindex;
2186
2187 ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex;
2188 gw = &mlxsw_sp_rt6->rt->rt6i_gateway;
2189 if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex))
2190 return false;
2191 }
2192
2193 return true;
2194}
2195
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002196static int
2197mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
2198{
2199 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
2200 const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
2201
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002202 switch (cmp_arg->proto) {
2203 case MLXSW_SP_L3_PROTO_IPV4:
2204 return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
2205 case MLXSW_SP_L3_PROTO_IPV6:
2206 return !mlxsw_sp_nexthop6_group_cmp(nh_grp,
2207 cmp_arg->fib6_entry);
2208 default:
2209 WARN_ON(1);
2210 return 1;
2211 }
2212}
2213
2214static int
2215mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp)
2216{
2217 return nh_grp->neigh_tbl->family;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002218}
2219
2220static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
2221{
2222 const struct mlxsw_sp_nexthop_group *nh_grp = data;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002223 const struct mlxsw_sp_nexthop *nh;
2224 struct fib_info *fi;
2225 unsigned int val;
2226 int i;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002227
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002228 switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
2229 case AF_INET:
2230 fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
2231 return jhash(&fi, sizeof(fi), seed);
2232 case AF_INET6:
2233 val = nh_grp->count;
2234 for (i = 0; i < nh_grp->count; i++) {
2235 nh = &nh_grp->nexthops[i];
2236 val ^= nh->ifindex;
2237 }
2238 return jhash(&val, sizeof(val), seed);
2239 default:
2240 WARN_ON(1);
2241 return 0;
2242 }
2243}
2244
2245static u32
2246mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
2247{
2248 unsigned int val = fib6_entry->nrt6;
2249 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2250 struct net_device *dev;
2251
2252 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2253 dev = mlxsw_sp_rt6->rt->dst.dev;
2254 val ^= dev->ifindex;
2255 }
2256
2257 return jhash(&val, sizeof(val), seed);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002258}
2259
2260static u32
2261mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
2262{
2263 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
2264
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002265 switch (cmp_arg->proto) {
2266 case MLXSW_SP_L3_PROTO_IPV4:
2267 return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
2268 case MLXSW_SP_L3_PROTO_IPV6:
2269 return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);
2270 default:
2271 WARN_ON(1);
2272 return 0;
2273 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002274}
2275
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002276static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002277 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002278 .hashfn = mlxsw_sp_nexthop_group_hash,
2279 .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj,
2280 .obj_cmpfn = mlxsw_sp_nexthop_group_cmp,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002281};
2282
2283static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
2284 struct mlxsw_sp_nexthop_group *nh_grp)
2285{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002286 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
2287 !nh_grp->gateway)
2288 return 0;
2289
Ido Schimmel9011b672017-05-16 19:38:25 +02002290 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002291 &nh_grp->ht_node,
2292 mlxsw_sp_nexthop_group_ht_params);
2293}
2294
2295static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
2296 struct mlxsw_sp_nexthop_group *nh_grp)
2297{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002298 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
2299 !nh_grp->gateway)
2300 return;
2301
Ido Schimmel9011b672017-05-16 19:38:25 +02002302 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002303 &nh_grp->ht_node,
2304 mlxsw_sp_nexthop_group_ht_params);
2305}
2306
2307static struct mlxsw_sp_nexthop_group *
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002308mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
2309 struct fib_info *fi)
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002310{
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002311 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
2312
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002313 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002314 cmp_arg.fi = fi;
2315 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
2316 &cmp_arg,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002317 mlxsw_sp_nexthop_group_ht_params);
2318}
2319
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002320static struct mlxsw_sp_nexthop_group *
2321mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,
2322 struct mlxsw_sp_fib6_entry *fib6_entry)
2323{
2324 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
2325
2326 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6;
2327 cmp_arg.fib6_entry = fib6_entry;
2328 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
2329 &cmp_arg,
2330 mlxsw_sp_nexthop_group_ht_params);
2331}
2332
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002333static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
2334 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
2335 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
2336 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
2337};
2338
2339static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
2340 struct mlxsw_sp_nexthop *nh)
2341{
Ido Schimmel9011b672017-05-16 19:38:25 +02002342 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002343 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
2344}
2345
2346static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
2347 struct mlxsw_sp_nexthop *nh)
2348{
Ido Schimmel9011b672017-05-16 19:38:25 +02002349 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002350 mlxsw_sp_nexthop_ht_params);
2351}
2352
Ido Schimmelad178c82017-02-08 11:16:40 +01002353static struct mlxsw_sp_nexthop *
2354mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
2355 struct mlxsw_sp_nexthop_key key)
2356{
Ido Schimmel9011b672017-05-16 19:38:25 +02002357 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01002358 mlxsw_sp_nexthop_ht_params);
2359}
2360
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002361static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01002362 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002363 u32 adj_index, u16 ecmp_size,
2364 u32 new_adj_index,
2365 u16 new_ecmp_size)
2366{
2367 char raleu_pl[MLXSW_REG_RALEU_LEN];
2368
Ido Schimmel1a9234e662016-09-19 08:29:26 +02002369 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01002370 (enum mlxsw_reg_ralxx_protocol) fib->proto,
2371 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02002372 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002373 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
2374}
2375
2376static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
2377 struct mlxsw_sp_nexthop_group *nh_grp,
2378 u32 old_adj_index, u16 old_ecmp_size)
2379{
2380 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002381 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002382 int err;
2383
2384 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01002385 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002386 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002387 fib = fib_entry->fib_node->fib;
2388 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002389 old_adj_index,
2390 old_ecmp_size,
2391 nh_grp->adj_index,
2392 nh_grp->ecmp_size);
2393 if (err)
2394 return err;
2395 }
2396 return 0;
2397}
2398
2399static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
2400 struct mlxsw_sp_nexthop *nh)
2401{
2402 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
2403 char ratr_pl[MLXSW_REG_RATR_LEN];
2404
2405 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
Petr Machata89e41982017-09-02 23:49:15 +02002406 true, MLXSW_REG_RATR_TYPE_ETHERNET,
2407 adj_index, neigh_entry->rif);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002408 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
2409 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
2410}
2411
Petr Machata1012b9a2017-09-02 23:49:23 +02002412static int mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp,
2413 u32 adj_index,
2414 struct mlxsw_sp_nexthop *nh)
2415{
2416 const struct mlxsw_sp_ipip_ops *ipip_ops;
2417
2418 ipip_ops = mlxsw_sp->router->ipip_ops_arr[nh->ipip_entry->ipipt];
2419 return ipip_ops->nexthop_update(mlxsw_sp, adj_index, nh->ipip_entry);
2420}
2421
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002422static int
Petr Machata35225e42017-09-02 23:49:22 +02002423mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp,
2424 struct mlxsw_sp_nexthop_group *nh_grp,
2425 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002426{
2427 u32 adj_index = nh_grp->adj_index; /* base */
2428 struct mlxsw_sp_nexthop *nh;
2429 int i;
2430 int err;
2431
2432 for (i = 0; i < nh_grp->count; i++) {
2433 nh = &nh_grp->nexthops[i];
2434
2435 if (!nh->should_offload) {
2436 nh->offloaded = 0;
2437 continue;
2438 }
2439
Ido Schimmela59b7e02017-01-23 11:11:42 +01002440 if (nh->update || reallocate) {
Petr Machata35225e42017-09-02 23:49:22 +02002441 switch (nh->type) {
2442 case MLXSW_SP_NEXTHOP_TYPE_ETH:
2443 err = mlxsw_sp_nexthop_mac_update
2444 (mlxsw_sp, adj_index, nh);
2445 break;
Petr Machata1012b9a2017-09-02 23:49:23 +02002446 case MLXSW_SP_NEXTHOP_TYPE_IPIP:
2447 err = mlxsw_sp_nexthop_ipip_update
2448 (mlxsw_sp, adj_index, nh);
2449 break;
Petr Machata35225e42017-09-02 23:49:22 +02002450 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002451 if (err)
2452 return err;
2453 nh->update = 0;
2454 nh->offloaded = 1;
2455 }
2456 adj_index++;
2457 }
2458 return 0;
2459}
2460
Ido Schimmel1819ae32017-07-21 18:04:28 +02002461static bool
2462mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2463 const struct mlxsw_sp_fib_entry *fib_entry);
2464
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002465static int
2466mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
2467 struct mlxsw_sp_nexthop_group *nh_grp)
2468{
2469 struct mlxsw_sp_fib_entry *fib_entry;
2470 int err;
2471
2472 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02002473 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2474 fib_entry))
2475 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002476 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2477 if (err)
2478 return err;
2479 }
2480 return 0;
2481}
2482
2483static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02002484mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2485 enum mlxsw_reg_ralue_op op, int err);
2486
2487static void
2488mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
2489{
2490 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
2491 struct mlxsw_sp_fib_entry *fib_entry;
2492
2493 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
2494 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2495 fib_entry))
2496 continue;
2497 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2498 }
2499}
2500
2501static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002502mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
2503 struct mlxsw_sp_nexthop_group *nh_grp)
2504{
2505 struct mlxsw_sp_nexthop *nh;
2506 bool offload_change = false;
2507 u32 adj_index;
2508 u16 ecmp_size = 0;
2509 bool old_adj_index_valid;
2510 u32 old_adj_index;
2511 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002512 int i;
2513 int err;
2514
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002515 if (!nh_grp->gateway) {
2516 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2517 return;
2518 }
2519
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002520 for (i = 0; i < nh_grp->count; i++) {
2521 nh = &nh_grp->nexthops[i];
2522
Petr Machata56b8a9e2017-07-31 09:27:29 +02002523 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002524 offload_change = true;
2525 if (nh->should_offload)
2526 nh->update = 1;
2527 }
2528 if (nh->should_offload)
2529 ecmp_size++;
2530 }
2531 if (!offload_change) {
2532 /* Nothing was added or removed, so no need to reallocate. Just
2533 * update MAC on existing adjacency indexes.
2534 */
Petr Machata35225e42017-09-02 23:49:22 +02002535 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002536 if (err) {
2537 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2538 goto set_trap;
2539 }
2540 return;
2541 }
2542 if (!ecmp_size)
2543 /* No neigh of this group is connected so we just set
2544 * the trap and let everthing flow through kernel.
2545 */
2546 goto set_trap;
2547
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01002548 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
2549 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002550 /* We ran out of KVD linear space, just set the
2551 * trap and let everything flow through kernel.
2552 */
2553 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
2554 goto set_trap;
2555 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002556 old_adj_index_valid = nh_grp->adj_index_valid;
2557 old_adj_index = nh_grp->adj_index;
2558 old_ecmp_size = nh_grp->ecmp_size;
2559 nh_grp->adj_index_valid = 1;
2560 nh_grp->adj_index = adj_index;
2561 nh_grp->ecmp_size = ecmp_size;
Petr Machata35225e42017-09-02 23:49:22 +02002562 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002563 if (err) {
2564 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2565 goto set_trap;
2566 }
2567
2568 if (!old_adj_index_valid) {
2569 /* The trap was set for fib entries, so we have to call
2570 * fib entry update to unset it and use adjacency index.
2571 */
2572 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2573 if (err) {
2574 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
2575 goto set_trap;
2576 }
2577 return;
2578 }
2579
2580 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
2581 old_adj_index, old_ecmp_size);
2582 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
2583 if (err) {
2584 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
2585 goto set_trap;
2586 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02002587
2588 /* Offload state within the group changed, so update the flags. */
2589 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
2590
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002591 return;
2592
2593set_trap:
2594 old_adj_index_valid = nh_grp->adj_index_valid;
2595 nh_grp->adj_index_valid = 0;
2596 for (i = 0; i < nh_grp->count; i++) {
2597 nh = &nh_grp->nexthops[i];
2598 nh->offloaded = 0;
2599 }
2600 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2601 if (err)
2602 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
2603 if (old_adj_index_valid)
2604 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
2605}
2606
2607static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
2608 bool removing)
2609{
Petr Machata213666a2017-07-31 09:27:30 +02002610 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002611 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02002612 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002613 nh->should_offload = 0;
2614 nh->update = 1;
2615}
2616
2617static void
2618mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
2619 struct mlxsw_sp_neigh_entry *neigh_entry,
2620 bool removing)
2621{
2622 struct mlxsw_sp_nexthop *nh;
2623
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002624 list_for_each_entry(nh, &neigh_entry->nexthop_list,
2625 neigh_list_node) {
2626 __mlxsw_sp_nexthop_neigh_update(nh, removing);
2627 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2628 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002629}
2630
Ido Schimmel9665b742017-02-08 11:16:42 +01002631static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002632 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002633{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002634 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002635 return;
2636
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002637 nh->rif = rif;
2638 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01002639}
2640
2641static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
2642{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002643 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002644 return;
2645
2646 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002647 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01002648}
2649
Ido Schimmela8c97012017-02-08 11:16:35 +01002650static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
2651 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002652{
2653 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002654 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002655 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002656 int err;
2657
Ido Schimmelad178c82017-02-08 11:16:40 +01002658 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01002659 return 0;
2660
Jiri Pirko33b13412016-11-10 12:31:04 +01002661 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02002662 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01002663 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01002664 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01002665 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002666 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01002667 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002668 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
2669 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01002670 if (IS_ERR(n))
2671 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002672 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01002673 }
2674 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
2675 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002676 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
2677 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002678 err = -EINVAL;
2679 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002680 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002681 }
Yotam Gigib2157142016-07-05 11:27:51 +02002682
2683 /* If that is the first nexthop connected to that neigh, add to
2684 * nexthop_neighs_list
2685 */
2686 if (list_empty(&neigh_entry->nexthop_list))
2687 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02002688 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02002689
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002690 nh->neigh_entry = neigh_entry;
2691 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
2692 read_lock_bh(&n->lock);
2693 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002694 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002695 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01002696 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002697
2698 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002699
2700err_neigh_entry_create:
2701 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002702 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002703}
2704
Ido Schimmela8c97012017-02-08 11:16:35 +01002705static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
2706 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002707{
2708 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01002709 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002710
Ido Schimmelb8399a12017-02-08 11:16:33 +01002711 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01002712 return;
2713 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01002714
Ido Schimmel58312122016-12-23 09:32:50 +01002715 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002716 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01002717 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02002718
2719 /* If that is the last nexthop connected to that neigh, remove from
2720 * nexthop_neighs_list
2721 */
Ido Schimmele58be792017-02-08 11:16:28 +01002722 if (list_empty(&neigh_entry->nexthop_list))
2723 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02002724
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002725 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
2726 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
2727
2728 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01002729}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002730
Petr Machata6ddb7422017-09-02 23:49:19 +02002731static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp,
2732 const struct net_device *dev,
2733 enum mlxsw_sp_ipip_type *p_type)
2734{
2735 struct mlxsw_sp_router *router = mlxsw_sp->router;
2736 const struct mlxsw_sp_ipip_ops *ipip_ops;
2737 enum mlxsw_sp_ipip_type ipipt;
2738
2739 for (ipipt = 0; ipipt < MLXSW_SP_IPIP_TYPE_MAX; ++ipipt) {
2740 ipip_ops = router->ipip_ops_arr[ipipt];
2741 if (dev->type == ipip_ops->dev_type) {
2742 if (p_type)
2743 *p_type = ipipt;
2744 return true;
2745 }
2746 }
2747 return false;
2748}
2749
Petr Machata1012b9a2017-09-02 23:49:23 +02002750static int mlxsw_sp_nexthop_ipip_init(struct mlxsw_sp *mlxsw_sp,
2751 enum mlxsw_sp_ipip_type ipipt,
2752 struct mlxsw_sp_nexthop *nh,
2753 struct net_device *ol_dev)
2754{
2755 if (!nh->nh_grp->gateway || nh->ipip_entry)
2756 return 0;
2757
2758 nh->ipip_entry = mlxsw_sp_ipip_entry_get(mlxsw_sp, ipipt, ol_dev);
2759 if (IS_ERR(nh->ipip_entry))
2760 return PTR_ERR(nh->ipip_entry);
2761
2762 __mlxsw_sp_nexthop_neigh_update(nh, false);
2763 return 0;
2764}
2765
2766static void mlxsw_sp_nexthop_ipip_fini(struct mlxsw_sp *mlxsw_sp,
2767 struct mlxsw_sp_nexthop *nh)
2768{
2769 struct mlxsw_sp_ipip_entry *ipip_entry = nh->ipip_entry;
2770
2771 if (!ipip_entry)
2772 return;
2773
2774 __mlxsw_sp_nexthop_neigh_update(nh, true);
2775 mlxsw_sp_ipip_entry_put(mlxsw_sp, ipip_entry);
2776 nh->ipip_entry = NULL;
2777}
2778
2779static bool mlxsw_sp_nexthop4_ipip_type(const struct mlxsw_sp *mlxsw_sp,
2780 const struct fib_nh *fib_nh,
2781 enum mlxsw_sp_ipip_type *p_ipipt)
2782{
2783 struct net_device *dev = fib_nh->nh_dev;
2784
2785 return dev &&
2786 fib_nh->nh_parent->fib_type == RTN_UNICAST &&
2787 mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, p_ipipt);
2788}
2789
Petr Machata35225e42017-09-02 23:49:22 +02002790static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp,
2791 struct mlxsw_sp_nexthop *nh)
2792{
2793 switch (nh->type) {
2794 case MLXSW_SP_NEXTHOP_TYPE_ETH:
2795 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2796 mlxsw_sp_nexthop_rif_fini(nh);
2797 break;
Petr Machata1012b9a2017-09-02 23:49:23 +02002798 case MLXSW_SP_NEXTHOP_TYPE_IPIP:
2799 mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh);
2800 break;
Petr Machata35225e42017-09-02 23:49:22 +02002801 }
2802}
2803
2804static int mlxsw_sp_nexthop4_type_init(struct mlxsw_sp *mlxsw_sp,
2805 struct mlxsw_sp_nexthop *nh,
2806 struct fib_nh *fib_nh)
2807{
Petr Machata1012b9a2017-09-02 23:49:23 +02002808 struct mlxsw_sp_router *router = mlxsw_sp->router;
Petr Machata35225e42017-09-02 23:49:22 +02002809 struct net_device *dev = fib_nh->nh_dev;
Petr Machata1012b9a2017-09-02 23:49:23 +02002810 enum mlxsw_sp_ipip_type ipipt;
Petr Machata35225e42017-09-02 23:49:22 +02002811 struct mlxsw_sp_rif *rif;
2812 int err;
2813
Petr Machata1012b9a2017-09-02 23:49:23 +02002814 if (mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fib_nh, &ipipt) &&
2815 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev,
2816 MLXSW_SP_L3_PROTO_IPV4)) {
2817 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
2818 return mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev);
2819 }
2820
Petr Machata35225e42017-09-02 23:49:22 +02002821 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
2822 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
2823 if (!rif)
2824 return 0;
2825
2826 mlxsw_sp_nexthop_rif_init(nh, rif);
2827 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2828 if (err)
2829 goto err_neigh_init;
2830
2831 return 0;
2832
2833err_neigh_init:
2834 mlxsw_sp_nexthop_rif_fini(nh);
2835 return err;
2836}
2837
2838static void mlxsw_sp_nexthop4_type_fini(struct mlxsw_sp *mlxsw_sp,
2839 struct mlxsw_sp_nexthop *nh)
2840{
2841 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
2842}
2843
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002844static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
2845 struct mlxsw_sp_nexthop_group *nh_grp,
2846 struct mlxsw_sp_nexthop *nh,
2847 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002848{
2849 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002850 struct in_device *in_dev;
Ido Schimmela8c97012017-02-08 11:16:35 +01002851 int err;
2852
2853 nh->nh_grp = nh_grp;
2854 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002855 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01002856 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
2857 if (err)
2858 return err;
2859
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02002860 list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list);
2861
Ido Schimmel97989ee2017-03-10 08:53:38 +01002862 if (!dev)
2863 return 0;
2864
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002865 in_dev = __in_dev_get_rtnl(dev);
2866 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
2867 fib_nh->nh_flags & RTNH_F_LINKDOWN)
2868 return 0;
2869
Petr Machata35225e42017-09-02 23:49:22 +02002870 err = mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01002871 if (err)
2872 goto err_nexthop_neigh_init;
2873
2874 return 0;
2875
2876err_nexthop_neigh_init:
2877 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
2878 return err;
2879}
2880
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002881static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
2882 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002883{
Petr Machata35225e42017-09-02 23:49:22 +02002884 mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02002885 list_del(&nh->router_list_node);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002886 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002887}
2888
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002889static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
2890 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01002891{
2892 struct mlxsw_sp_nexthop_key key;
2893 struct mlxsw_sp_nexthop *nh;
Ido Schimmelad178c82017-02-08 11:16:40 +01002894
Ido Schimmel9011b672017-05-16 19:38:25 +02002895 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01002896 return;
2897
2898 key.fib_nh = fib_nh;
2899 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
2900 if (WARN_ON_ONCE(!nh))
2901 return;
2902
Ido Schimmelad178c82017-02-08 11:16:40 +01002903 switch (event) {
2904 case FIB_EVENT_NH_ADD:
Petr Machata35225e42017-09-02 23:49:22 +02002905 mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002906 break;
2907 case FIB_EVENT_NH_DEL:
Petr Machata35225e42017-09-02 23:49:22 +02002908 mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002909 break;
2910 }
2911
2912 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2913}
2914
Ido Schimmel9665b742017-02-08 11:16:42 +01002915static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002916 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002917{
2918 struct mlxsw_sp_nexthop *nh, *tmp;
2919
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002920 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Petr Machata35225e42017-09-02 23:49:22 +02002921 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002922 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2923 }
2924}
2925
Petr Machata9b014512017-09-02 23:49:20 +02002926static bool mlxsw_sp_fi_is_gateway(const struct mlxsw_sp *mlxsw_sp,
2927 const struct fib_info *fi)
2928{
Petr Machata1012b9a2017-09-02 23:49:23 +02002929 return fi->fib_nh->nh_scope == RT_SCOPE_LINK ||
2930 mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fi->fib_nh, NULL);
Petr Machata9b014512017-09-02 23:49:20 +02002931}
2932
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002933static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002934mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002935{
2936 struct mlxsw_sp_nexthop_group *nh_grp;
2937 struct mlxsw_sp_nexthop *nh;
2938 struct fib_nh *fib_nh;
2939 size_t alloc_size;
2940 int i;
2941 int err;
2942
2943 alloc_size = sizeof(*nh_grp) +
2944 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2945 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2946 if (!nh_grp)
2947 return ERR_PTR(-ENOMEM);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002948 nh_grp->priv = fi;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002949 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002950 nh_grp->neigh_tbl = &arp_tbl;
2951
Petr Machata9b014512017-09-02 23:49:20 +02002952 nh_grp->gateway = mlxsw_sp_fi_is_gateway(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002953 nh_grp->count = fi->fib_nhs;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002954 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002955 for (i = 0; i < nh_grp->count; i++) {
2956 nh = &nh_grp->nexthops[i];
2957 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002958 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002959 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002960 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002961 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002962 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2963 if (err)
2964 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002965 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2966 return nh_grp;
2967
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002968err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002969err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002970 for (i--; i >= 0; i--) {
2971 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002972 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002973 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002974 fib_info_put(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002975 kfree(nh_grp);
2976 return ERR_PTR(err);
2977}
2978
2979static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002980mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2981 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002982{
2983 struct mlxsw_sp_nexthop *nh;
2984 int i;
2985
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002986 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002987 for (i = 0; i < nh_grp->count; i++) {
2988 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002989 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002990 }
Ido Schimmel58312122016-12-23 09:32:50 +01002991 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2992 WARN_ON_ONCE(nh_grp->adj_index_valid);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002993 fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002994 kfree(nh_grp);
2995}
2996
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002997static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2998 struct mlxsw_sp_fib_entry *fib_entry,
2999 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003000{
3001 struct mlxsw_sp_nexthop_group *nh_grp;
3002
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02003003 nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003004 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003005 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003006 if (IS_ERR(nh_grp))
3007 return PTR_ERR(nh_grp);
3008 }
3009 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
3010 fib_entry->nh_group = nh_grp;
3011 return 0;
3012}
3013
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003014static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
3015 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003016{
3017 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3018
3019 list_del(&fib_entry->nexthop_group_node);
3020 if (!list_empty(&nh_grp->fib_list))
3021 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003022 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003023}
3024
Ido Schimmel013b20f2017-02-08 11:16:36 +01003025static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003026mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
3027{
3028 struct mlxsw_sp_fib4_entry *fib4_entry;
3029
3030 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
3031 common);
3032 return !fib4_entry->tos;
3033}
3034
3035static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01003036mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
3037{
3038 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
3039
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003040 switch (fib_entry->fib_node->fib->proto) {
3041 case MLXSW_SP_L3_PROTO_IPV4:
3042 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
3043 return false;
3044 break;
3045 case MLXSW_SP_L3_PROTO_IPV6:
3046 break;
3047 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01003048
Ido Schimmel013b20f2017-02-08 11:16:36 +01003049 switch (fib_entry->type) {
3050 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
3051 return !!nh_group->adj_index_valid;
3052 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01003053 return !!nh_group->nh_rif;
Petr Machata4607f6d2017-09-02 23:49:25 +02003054 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
3055 return true;
Ido Schimmel013b20f2017-02-08 11:16:36 +01003056 default:
3057 return false;
3058 }
3059}
3060
Ido Schimmel428b8512017-08-03 13:28:28 +02003061static struct mlxsw_sp_nexthop *
3062mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
3063 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3064{
3065 int i;
3066
3067 for (i = 0; i < nh_grp->count; i++) {
3068 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3069 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3070
3071 if (nh->rif && nh->rif->dev == rt->dst.dev &&
3072 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
3073 &rt->rt6i_gateway))
3074 return nh;
3075 continue;
3076 }
3077
3078 return NULL;
3079}
3080
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003081static void
3082mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3083{
3084 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3085 int i;
3086
Petr Machata4607f6d2017-09-02 23:49:25 +02003087 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL ||
3088 fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP) {
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003089 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
3090 return;
3091 }
3092
3093 for (i = 0; i < nh_grp->count; i++) {
3094 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3095
3096 if (nh->offloaded)
3097 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
3098 else
3099 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
3100 }
3101}
3102
3103static void
3104mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3105{
3106 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3107 int i;
3108
3109 for (i = 0; i < nh_grp->count; i++) {
3110 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3111
3112 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
3113 }
3114}
3115
Ido Schimmel428b8512017-08-03 13:28:28 +02003116static void
3117mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3118{
3119 struct mlxsw_sp_fib6_entry *fib6_entry;
3120 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3121
3122 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
3123 common);
3124
3125 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
3126 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
Ido Schimmelfe400792017-08-15 09:09:49 +02003127 list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003128 return;
3129 }
3130
3131 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3132 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3133 struct mlxsw_sp_nexthop *nh;
3134
3135 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
3136 if (nh && nh->offloaded)
Ido Schimmelfe400792017-08-15 09:09:49 +02003137 mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003138 else
Ido Schimmelfe400792017-08-15 09:09:49 +02003139 mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003140 }
3141}
3142
3143static void
3144mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3145{
3146 struct mlxsw_sp_fib6_entry *fib6_entry;
3147 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3148
3149 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
3150 common);
3151 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3152 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3153
Ido Schimmelfe400792017-08-15 09:09:49 +02003154 rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003155 }
3156}
3157
Ido Schimmel013b20f2017-02-08 11:16:36 +01003158static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3159{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003160 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01003161 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003162 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003163 break;
3164 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003165 mlxsw_sp_fib6_entry_offload_set(fib_entry);
3166 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01003167 }
3168}
3169
3170static void
3171mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3172{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003173 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01003174 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003175 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003176 break;
3177 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003178 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
3179 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01003180 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01003181}
3182
3183static void
3184mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
3185 enum mlxsw_reg_ralue_op op, int err)
3186{
3187 switch (op) {
3188 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01003189 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
3190 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
3191 if (err)
3192 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02003193 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01003194 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02003195 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01003196 mlxsw_sp_fib_entry_offload_unset(fib_entry);
3197 return;
3198 default:
3199 return;
3200 }
3201}
3202
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003203static void
3204mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
3205 const struct mlxsw_sp_fib_entry *fib_entry,
3206 enum mlxsw_reg_ralue_op op)
3207{
3208 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
3209 enum mlxsw_reg_ralxx_protocol proto;
3210 u32 *p_dip;
3211
3212 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
3213
3214 switch (fib->proto) {
3215 case MLXSW_SP_L3_PROTO_IPV4:
3216 p_dip = (u32 *) fib_entry->fib_node->key.addr;
3217 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
3218 fib_entry->fib_node->key.prefix_len,
3219 *p_dip);
3220 break;
3221 case MLXSW_SP_L3_PROTO_IPV6:
3222 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
3223 fib_entry->fib_node->key.prefix_len,
3224 fib_entry->fib_node->key.addr);
3225 break;
3226 }
3227}
3228
3229static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
3230 struct mlxsw_sp_fib_entry *fib_entry,
3231 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003232{
3233 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003234 enum mlxsw_reg_ralue_trap_action trap_action;
3235 u16 trap_id = 0;
3236 u32 adjacency_index = 0;
3237 u16 ecmp_size = 0;
3238
3239 /* In case the nexthop group adjacency index is valid, use it
3240 * with provided ECMP size. Otherwise, setup trap and pass
3241 * traffic to kernel.
3242 */
Ido Schimmel4b411472017-02-08 11:16:37 +01003243 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003244 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
3245 adjacency_index = fib_entry->nh_group->adj_index;
3246 ecmp_size = fib_entry->nh_group->ecmp_size;
3247 } else {
3248 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
3249 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
3250 }
3251
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003252 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003253 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
3254 adjacency_index, ecmp_size);
3255 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3256}
3257
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003258static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
3259 struct mlxsw_sp_fib_entry *fib_entry,
3260 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003261{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003262 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003263 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003264 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01003265 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003266 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003267
3268 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
3269 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003270 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003271 } else {
3272 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
3273 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
3274 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003275
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003276 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003277 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
3278 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003279 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3280}
3281
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003282static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
3283 struct mlxsw_sp_fib_entry *fib_entry,
3284 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003285{
3286 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02003287
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003288 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003289 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
3290 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3291}
3292
Petr Machata4607f6d2017-09-02 23:49:25 +02003293static int
3294mlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp,
3295 struct mlxsw_sp_fib_entry *fib_entry,
3296 enum mlxsw_reg_ralue_op op)
3297{
3298 struct mlxsw_sp_ipip_entry *ipip_entry = fib_entry->decap.ipip_entry;
3299 const struct mlxsw_sp_ipip_ops *ipip_ops;
3300
3301 if (WARN_ON(!ipip_entry))
3302 return -EINVAL;
3303
3304 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
3305 return ipip_ops->fib_entry_op(mlxsw_sp, ipip_entry, op,
3306 fib_entry->decap.tunnel_index);
3307}
3308
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003309static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
3310 struct mlxsw_sp_fib_entry *fib_entry,
3311 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003312{
3313 switch (fib_entry->type) {
3314 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003315 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003316 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003317 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003318 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003319 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Petr Machata4607f6d2017-09-02 23:49:25 +02003320 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
3321 return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp,
3322 fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003323 }
3324 return -EINVAL;
3325}
3326
3327static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
3328 struct mlxsw_sp_fib_entry *fib_entry,
3329 enum mlxsw_reg_ralue_op op)
3330{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003331 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003332
Ido Schimmel013b20f2017-02-08 11:16:36 +01003333 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003334
Ido Schimmel013b20f2017-02-08 11:16:36 +01003335 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003336}
3337
3338static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
3339 struct mlxsw_sp_fib_entry *fib_entry)
3340{
Jiri Pirko7146da32016-09-01 10:37:41 +02003341 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
3342 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003343}
3344
3345static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
3346 struct mlxsw_sp_fib_entry *fib_entry)
3347{
3348 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
3349 MLXSW_REG_RALUE_OP_WRITE_DELETE);
3350}
3351
Jiri Pirko61c503f2016-07-04 08:23:11 +02003352static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01003353mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
3354 const struct fib_entry_notifier_info *fen_info,
3355 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003356{
Petr Machata4607f6d2017-09-02 23:49:25 +02003357 union mlxsw_sp_l3addr dip = { .addr4 = htonl(fen_info->dst) };
3358 struct net_device *dev = fen_info->fi->fib_dev;
3359 struct mlxsw_sp_ipip_entry *ipip_entry;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003360 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003361
Ido Schimmel97989ee2017-03-10 08:53:38 +01003362 switch (fen_info->type) {
Ido Schimmel97989ee2017-03-10 08:53:38 +01003363 case RTN_LOCAL:
Petr Machata4607f6d2017-09-02 23:49:25 +02003364 ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, dev,
3365 MLXSW_SP_L3_PROTO_IPV4, dip);
3366 if (ipip_entry) {
3367 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
3368 return mlxsw_sp_fib_entry_decap_init(mlxsw_sp,
3369 fib_entry,
3370 ipip_entry);
3371 }
3372 /* fall through */
3373 case RTN_BROADCAST:
Jiri Pirko61c503f2016-07-04 08:23:11 +02003374 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3375 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003376 case RTN_UNREACHABLE: /* fall through */
3377 case RTN_BLACKHOLE: /* fall through */
3378 case RTN_PROHIBIT:
3379 /* Packets hitting these routes need to be trapped, but
3380 * can do so with a lower priority than packets directed
3381 * at the host, so use action type local instead of trap.
3382 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003383 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003384 return 0;
3385 case RTN_UNICAST:
Petr Machata9b014512017-09-02 23:49:20 +02003386 if (mlxsw_sp_fi_is_gateway(mlxsw_sp, fi))
Ido Schimmel97989ee2017-03-10 08:53:38 +01003387 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
Petr Machata9b014512017-09-02 23:49:20 +02003388 else
3389 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003390 return 0;
3391 default:
3392 return -EINVAL;
3393 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003394}
3395
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003396static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003397mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
3398 struct mlxsw_sp_fib_node *fib_node,
3399 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02003400{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003401 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02003402 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003403 int err;
3404
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003405 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
3406 if (!fib4_entry)
3407 return ERR_PTR(-ENOMEM);
3408 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003409
3410 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
3411 if (err)
3412 goto err_fib4_entry_type_set;
3413
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003414 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003415 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003416 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003417
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003418 fib4_entry->prio = fen_info->fi->fib_priority;
3419 fib4_entry->tb_id = fen_info->tb_id;
3420 fib4_entry->type = fen_info->type;
3421 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003422
3423 fib_entry->fib_node = fib_node;
3424
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003425 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003426
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003427err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01003428err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003429 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003430 return ERR_PTR(err);
3431}
3432
3433static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003434 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003435{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003436 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003437 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003438}
3439
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003440static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003441mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3442 const struct fib_entry_notifier_info *fen_info)
3443{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003444 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003445 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02003446 struct mlxsw_sp_fib *fib;
3447 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003448
Ido Schimmel160e22a2017-07-18 10:10:20 +02003449 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
3450 if (!vr)
3451 return NULL;
3452 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
3453
3454 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
3455 sizeof(fen_info->dst),
3456 fen_info->dst_len);
3457 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003458 return NULL;
3459
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003460 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3461 if (fib4_entry->tb_id == fen_info->tb_id &&
3462 fib4_entry->tos == fen_info->tos &&
3463 fib4_entry->type == fen_info->type &&
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02003464 mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
3465 fen_info->fi) {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003466 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003467 }
3468 }
3469
3470 return NULL;
3471}
3472
3473static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
3474 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
3475 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
3476 .key_len = sizeof(struct mlxsw_sp_fib_key),
3477 .automatic_shrinking = true,
3478};
3479
3480static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
3481 struct mlxsw_sp_fib_node *fib_node)
3482{
3483 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
3484 mlxsw_sp_fib_ht_params);
3485}
3486
3487static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
3488 struct mlxsw_sp_fib_node *fib_node)
3489{
3490 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
3491 mlxsw_sp_fib_ht_params);
3492}
3493
3494static struct mlxsw_sp_fib_node *
3495mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
3496 size_t addr_len, unsigned char prefix_len)
3497{
3498 struct mlxsw_sp_fib_key key;
3499
3500 memset(&key, 0, sizeof(key));
3501 memcpy(key.addr, addr, addr_len);
3502 key.prefix_len = prefix_len;
3503 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
3504}
3505
3506static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01003507mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01003508 size_t addr_len, unsigned char prefix_len)
3509{
3510 struct mlxsw_sp_fib_node *fib_node;
3511
3512 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
3513 if (!fib_node)
3514 return NULL;
3515
3516 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003517 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003518 memcpy(fib_node->key.addr, addr, addr_len);
3519 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003520
3521 return fib_node;
3522}
3523
3524static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
3525{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003526 list_del(&fib_node->list);
3527 WARN_ON(!list_empty(&fib_node->entry_list));
3528 kfree(fib_node);
3529}
3530
3531static bool
3532mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
3533 const struct mlxsw_sp_fib_entry *fib_entry)
3534{
3535 return list_first_entry(&fib_node->entry_list,
3536 struct mlxsw_sp_fib_entry, list) == fib_entry;
3537}
3538
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003539static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
3540 struct mlxsw_sp_fib *fib,
3541 struct mlxsw_sp_fib_node *fib_node)
3542{
3543 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
3544 struct mlxsw_sp_lpm_tree *lpm_tree;
3545 int err;
3546
3547 /* Since the tree is shared between all virtual routers we must
3548 * make sure it contains all the required prefix lengths. This
3549 * can be computed by either adding the new prefix length to the
3550 * existing prefix usage of a bound tree, or by aggregating the
3551 * prefix lengths across all virtual routers and adding the new
3552 * one as well.
3553 */
3554 if (fib->lpm_tree)
3555 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
3556 &fib->lpm_tree->prefix_usage);
3557 else
3558 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
3559 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
3560
3561 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
3562 fib->proto);
3563 if (IS_ERR(lpm_tree))
3564 return PTR_ERR(lpm_tree);
3565
3566 if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id)
3567 return 0;
3568
3569 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
3570 if (err)
3571 return err;
3572
3573 return 0;
3574}
3575
3576static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
3577 struct mlxsw_sp_fib *fib)
3578{
3579 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
3580 struct mlxsw_sp_lpm_tree *lpm_tree;
3581
3582 /* Aggregate prefix lengths across all virtual routers to make
3583 * sure we only have used prefix lengths in the LPM tree.
3584 */
3585 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
3586 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
3587 fib->proto);
3588 if (IS_ERR(lpm_tree))
3589 goto err_tree_get;
3590 mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
3591
3592err_tree_get:
3593 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage))
3594 return;
3595 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
3596 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
3597 fib->lpm_tree = NULL;
3598}
3599
Ido Schimmel9aecce12017-02-09 10:28:42 +01003600static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
3601{
3602 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003603 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003604
3605 if (fib->prefix_ref_count[prefix_len]++ == 0)
3606 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
3607}
3608
3609static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
3610{
3611 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003612 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003613
3614 if (--fib->prefix_ref_count[prefix_len] == 0)
3615 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
3616}
3617
Ido Schimmel76610eb2017-03-10 08:53:41 +01003618static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
3619 struct mlxsw_sp_fib_node *fib_node,
3620 struct mlxsw_sp_fib *fib)
3621{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003622 int err;
3623
3624 err = mlxsw_sp_fib_node_insert(fib, fib_node);
3625 if (err)
3626 return err;
3627 fib_node->fib = fib;
3628
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003629 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node);
3630 if (err)
3631 goto err_fib_lpm_tree_link;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003632
3633 mlxsw_sp_fib_node_prefix_inc(fib_node);
3634
3635 return 0;
3636
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003637err_fib_lpm_tree_link:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003638 fib_node->fib = NULL;
3639 mlxsw_sp_fib_node_remove(fib, fib_node);
3640 return err;
3641}
3642
3643static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
3644 struct mlxsw_sp_fib_node *fib_node)
3645{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003646 struct mlxsw_sp_fib *fib = fib_node->fib;
3647
3648 mlxsw_sp_fib_node_prefix_dec(fib_node);
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003649 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003650 fib_node->fib = NULL;
3651 mlxsw_sp_fib_node_remove(fib, fib_node);
3652}
3653
Ido Schimmel9aecce12017-02-09 10:28:42 +01003654static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003655mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
3656 size_t addr_len, unsigned char prefix_len,
3657 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003658{
3659 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003660 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02003661 struct mlxsw_sp_vr *vr;
3662 int err;
3663
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003664 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02003665 if (IS_ERR(vr))
3666 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003667 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02003668
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003669 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003670 if (fib_node)
3671 return fib_node;
3672
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003673 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003674 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02003675 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003676 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02003677 }
Jiri Pirko5b004412016-09-01 10:37:40 +02003678
Ido Schimmel76610eb2017-03-10 08:53:41 +01003679 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
3680 if (err)
3681 goto err_fib_node_init;
3682
Ido Schimmel9aecce12017-02-09 10:28:42 +01003683 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003684
Ido Schimmel76610eb2017-03-10 08:53:41 +01003685err_fib_node_init:
3686 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003687err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003688 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003689 return ERR_PTR(err);
3690}
3691
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003692static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
3693 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02003694{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003695 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02003696
Ido Schimmel9aecce12017-02-09 10:28:42 +01003697 if (!list_empty(&fib_node->entry_list))
3698 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003699 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003700 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003701 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003702}
3703
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003704static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003705mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003706 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003707{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003708 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003709
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003710 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3711 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003712 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003713 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003714 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003715 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003716 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003717 if (fib4_entry->prio >= new4_entry->prio ||
3718 fib4_entry->tos < new4_entry->tos)
3719 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003720 }
3721
3722 return NULL;
3723}
3724
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003725static int
3726mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
3727 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003728{
3729 struct mlxsw_sp_fib_node *fib_node;
3730
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003731 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01003732 return -EINVAL;
3733
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003734 fib_node = fib4_entry->common.fib_node;
3735 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
3736 common.list) {
3737 if (fib4_entry->tb_id != new4_entry->tb_id ||
3738 fib4_entry->tos != new4_entry->tos ||
3739 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003740 break;
3741 }
3742
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003743 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01003744 return 0;
3745}
3746
Ido Schimmel9aecce12017-02-09 10:28:42 +01003747static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02003748mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003749 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003750{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003751 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003752 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003753
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003754 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003755
Ido Schimmel4283bce2017-02-09 10:28:43 +01003756 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003757 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
3758 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003759 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003760
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003761 /* Insert new entry before replaced one, so that we can later
3762 * remove the second.
3763 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003764 if (fib4_entry) {
3765 list_add_tail(&new4_entry->common.list,
3766 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003767 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003768 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003769
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003770 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3771 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003772 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003773 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003774 }
3775
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003776 if (fib4_entry)
3777 list_add(&new4_entry->common.list,
3778 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003779 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003780 list_add(&new4_entry->common.list,
3781 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003782 }
3783
3784 return 0;
3785}
3786
3787static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003788mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003789{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003790 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003791}
3792
Ido Schimmel80c238f2017-07-18 10:10:29 +02003793static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
3794 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003795{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003796 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3797
Ido Schimmel9aecce12017-02-09 10:28:42 +01003798 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3799 return 0;
3800
3801 /* To prevent packet loss, overwrite the previously offloaded
3802 * entry.
3803 */
3804 if (!list_is_singular(&fib_node->entry_list)) {
3805 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3806 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3807
3808 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
3809 }
3810
3811 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
3812}
3813
Ido Schimmel80c238f2017-07-18 10:10:29 +02003814static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
3815 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003816{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003817 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3818
Ido Schimmel9aecce12017-02-09 10:28:42 +01003819 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3820 return;
3821
3822 /* Promote the next entry by overwriting the deleted entry */
3823 if (!list_is_singular(&fib_node->entry_list)) {
3824 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3825 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3826
3827 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
3828 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
3829 return;
3830 }
3831
3832 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
3833}
3834
3835static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003836 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003837 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003838{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003839 int err;
3840
Ido Schimmel9efbee62017-07-18 10:10:28 +02003841 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003842 if (err)
3843 return err;
3844
Ido Schimmel80c238f2017-07-18 10:10:29 +02003845 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003846 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02003847 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003848
Ido Schimmel9aecce12017-02-09 10:28:42 +01003849 return 0;
3850
Ido Schimmel80c238f2017-07-18 10:10:29 +02003851err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003852 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003853 return err;
3854}
3855
3856static void
3857mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003858 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003859{
Ido Schimmel80c238f2017-07-18 10:10:29 +02003860 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003861 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Petr Machata4607f6d2017-09-02 23:49:25 +02003862
3863 if (fib4_entry->common.type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP)
3864 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003865}
3866
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003867static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003868 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003869 bool replace)
3870{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003871 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
3872 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003873
3874 if (!replace)
3875 return;
3876
3877 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003878 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003879
3880 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
3881 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003882 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003883}
3884
Ido Schimmel9aecce12017-02-09 10:28:42 +01003885static int
3886mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01003887 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003888 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003889{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003890 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003891 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003892 int err;
3893
Ido Schimmel9011b672017-05-16 19:38:25 +02003894 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003895 return 0;
3896
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003897 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
3898 &fen_info->dst, sizeof(fen_info->dst),
3899 fen_info->dst_len,
3900 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003901 if (IS_ERR(fib_node)) {
3902 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
3903 return PTR_ERR(fib_node);
3904 }
3905
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003906 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
3907 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003908 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003909 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003910 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003911 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003912
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003913 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003914 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003915 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003916 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
3917 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003918 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01003919
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003920 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003921
Jiri Pirko61c503f2016-07-04 08:23:11 +02003922 return 0;
3923
Ido Schimmel9aecce12017-02-09 10:28:42 +01003924err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003925 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003926err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003927 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003928 return err;
3929}
3930
Jiri Pirko37956d72016-10-20 16:05:43 +02003931static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
3932 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003933{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003934 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003935 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003936
Ido Schimmel9011b672017-05-16 19:38:25 +02003937 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02003938 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003939
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003940 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
3941 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02003942 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003943 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003944
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003945 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3946 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003947 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003948}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003949
Ido Schimmel428b8512017-08-03 13:28:28 +02003950static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
3951{
3952 /* Packets with link-local destination IP arriving to the router
3953 * are trapped to the CPU, so no need to program specific routes
3954 * for them.
3955 */
3956 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
3957 return true;
3958
3959 /* Multicast routes aren't supported, so ignore them. Neighbour
3960 * Discovery packets are specifically trapped.
3961 */
3962 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
3963 return true;
3964
3965 /* Cloned routes are irrelevant in the forwarding path. */
3966 if (rt->rt6i_flags & RTF_CACHE)
3967 return true;
3968
3969 return false;
3970}
3971
3972static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
3973{
3974 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3975
3976 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
3977 if (!mlxsw_sp_rt6)
3978 return ERR_PTR(-ENOMEM);
3979
3980 /* In case of route replace, replaced route is deleted with
3981 * no notification. Take reference to prevent accessing freed
3982 * memory.
3983 */
3984 mlxsw_sp_rt6->rt = rt;
3985 rt6_hold(rt);
3986
3987 return mlxsw_sp_rt6;
3988}
3989
3990#if IS_ENABLED(CONFIG_IPV6)
3991static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3992{
3993 rt6_release(rt);
3994}
3995#else
3996static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3997{
3998}
3999#endif
4000
4001static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
4002{
4003 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
4004 kfree(mlxsw_sp_rt6);
4005}
4006
4007static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
4008{
4009 /* RTF_CACHE routes are ignored */
4010 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
4011}
4012
4013static struct rt6_info *
4014mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
4015{
4016 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
4017 list)->rt;
4018}
4019
4020static struct mlxsw_sp_fib6_entry *
4021mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004022 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004023{
4024 struct mlxsw_sp_fib6_entry *fib6_entry;
4025
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004026 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004027 return NULL;
4028
4029 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4030 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4031
4032 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
4033 * virtual router.
4034 */
4035 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
4036 continue;
4037 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
4038 break;
4039 if (rt->rt6i_metric < nrt->rt6i_metric)
4040 continue;
4041 if (rt->rt6i_metric == nrt->rt6i_metric &&
4042 mlxsw_sp_fib6_rt_can_mp(rt))
4043 return fib6_entry;
4044 if (rt->rt6i_metric > nrt->rt6i_metric)
4045 break;
4046 }
4047
4048 return NULL;
4049}
4050
4051static struct mlxsw_sp_rt6 *
4052mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
4053 const struct rt6_info *rt)
4054{
4055 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4056
4057 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
4058 if (mlxsw_sp_rt6->rt == rt)
4059 return mlxsw_sp_rt6;
4060 }
4061
4062 return NULL;
4063}
4064
Petr Machata8f28a302017-09-02 23:49:24 +02004065static bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp,
4066 const struct rt6_info *rt,
4067 enum mlxsw_sp_ipip_type *ret)
4068{
4069 return rt->dst.dev &&
4070 mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->dst.dev, ret);
4071}
4072
Petr Machata35225e42017-09-02 23:49:22 +02004073static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp,
4074 struct mlxsw_sp_nexthop_group *nh_grp,
4075 struct mlxsw_sp_nexthop *nh,
4076 const struct rt6_info *rt)
Ido Schimmel428b8512017-08-03 13:28:28 +02004077{
Petr Machata8f28a302017-09-02 23:49:24 +02004078 struct mlxsw_sp_router *router = mlxsw_sp->router;
Ido Schimmel428b8512017-08-03 13:28:28 +02004079 struct net_device *dev = rt->dst.dev;
Petr Machata8f28a302017-09-02 23:49:24 +02004080 enum mlxsw_sp_ipip_type ipipt;
Ido Schimmel428b8512017-08-03 13:28:28 +02004081 struct mlxsw_sp_rif *rif;
4082 int err;
4083
Petr Machata8f28a302017-09-02 23:49:24 +02004084 if (mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, &ipipt) &&
4085 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev,
4086 MLXSW_SP_L3_PROTO_IPV6)) {
4087 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
4088 return mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev);
4089 }
4090
Petr Machata35225e42017-09-02 23:49:22 +02004091 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
Ido Schimmel428b8512017-08-03 13:28:28 +02004092 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4093 if (!rif)
4094 return 0;
4095 mlxsw_sp_nexthop_rif_init(nh, rif);
4096
4097 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
4098 if (err)
4099 goto err_nexthop_neigh_init;
4100
4101 return 0;
4102
4103err_nexthop_neigh_init:
4104 mlxsw_sp_nexthop_rif_fini(nh);
4105 return err;
4106}
4107
Petr Machata35225e42017-09-02 23:49:22 +02004108static void mlxsw_sp_nexthop6_type_fini(struct mlxsw_sp *mlxsw_sp,
4109 struct mlxsw_sp_nexthop *nh)
4110{
4111 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
4112}
4113
4114static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
4115 struct mlxsw_sp_nexthop_group *nh_grp,
4116 struct mlxsw_sp_nexthop *nh,
4117 const struct rt6_info *rt)
4118{
4119 struct net_device *dev = rt->dst.dev;
4120
4121 nh->nh_grp = nh_grp;
4122 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
4123
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02004124 list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list);
4125
Petr Machata35225e42017-09-02 23:49:22 +02004126 if (!dev)
4127 return 0;
4128 nh->ifindex = dev->ifindex;
4129
4130 return mlxsw_sp_nexthop6_type_init(mlxsw_sp, nh_grp, nh, rt);
4131}
4132
Ido Schimmel428b8512017-08-03 13:28:28 +02004133static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
4134 struct mlxsw_sp_nexthop *nh)
4135{
Petr Machata35225e42017-09-02 23:49:22 +02004136 mlxsw_sp_nexthop6_type_fini(mlxsw_sp, nh);
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02004137 list_del(&nh->router_list_node);
Ido Schimmel428b8512017-08-03 13:28:28 +02004138}
4139
Petr Machataf6050ee2017-09-02 23:49:21 +02004140static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp,
4141 const struct rt6_info *rt)
4142{
Petr Machata8f28a302017-09-02 23:49:24 +02004143 return rt->rt6i_flags & RTF_GATEWAY ||
4144 mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, NULL);
Petr Machataf6050ee2017-09-02 23:49:21 +02004145}
4146
Ido Schimmel428b8512017-08-03 13:28:28 +02004147static struct mlxsw_sp_nexthop_group *
4148mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
4149 struct mlxsw_sp_fib6_entry *fib6_entry)
4150{
4151 struct mlxsw_sp_nexthop_group *nh_grp;
4152 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4153 struct mlxsw_sp_nexthop *nh;
4154 size_t alloc_size;
4155 int i = 0;
4156 int err;
4157
4158 alloc_size = sizeof(*nh_grp) +
4159 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
4160 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
4161 if (!nh_grp)
4162 return ERR_PTR(-ENOMEM);
4163 INIT_LIST_HEAD(&nh_grp->fib_list);
4164#if IS_ENABLED(CONFIG_IPV6)
4165 nh_grp->neigh_tbl = &nd_tbl;
4166#endif
4167 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
4168 struct mlxsw_sp_rt6, list);
Petr Machataf6050ee2017-09-02 23:49:21 +02004169 nh_grp->gateway = mlxsw_sp_rt6_is_gateway(mlxsw_sp, mlxsw_sp_rt6->rt);
Ido Schimmel428b8512017-08-03 13:28:28 +02004170 nh_grp->count = fib6_entry->nrt6;
4171 for (i = 0; i < nh_grp->count; i++) {
4172 struct rt6_info *rt = mlxsw_sp_rt6->rt;
4173
4174 nh = &nh_grp->nexthops[i];
4175 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
4176 if (err)
4177 goto err_nexthop6_init;
4178 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
4179 }
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004180
4181 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
4182 if (err)
4183 goto err_nexthop_group_insert;
4184
Ido Schimmel428b8512017-08-03 13:28:28 +02004185 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
4186 return nh_grp;
4187
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004188err_nexthop_group_insert:
Ido Schimmel428b8512017-08-03 13:28:28 +02004189err_nexthop6_init:
4190 for (i--; i >= 0; i--) {
4191 nh = &nh_grp->nexthops[i];
4192 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
4193 }
4194 kfree(nh_grp);
4195 return ERR_PTR(err);
4196}
4197
4198static void
4199mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
4200 struct mlxsw_sp_nexthop_group *nh_grp)
4201{
4202 struct mlxsw_sp_nexthop *nh;
4203 int i = nh_grp->count;
4204
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004205 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Ido Schimmel428b8512017-08-03 13:28:28 +02004206 for (i--; i >= 0; i--) {
4207 nh = &nh_grp->nexthops[i];
4208 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
4209 }
4210 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
4211 WARN_ON(nh_grp->adj_index_valid);
4212 kfree(nh_grp);
4213}
4214
4215static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
4216 struct mlxsw_sp_fib6_entry *fib6_entry)
4217{
4218 struct mlxsw_sp_nexthop_group *nh_grp;
4219
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004220 nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry);
4221 if (!nh_grp) {
4222 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
4223 if (IS_ERR(nh_grp))
4224 return PTR_ERR(nh_grp);
4225 }
Ido Schimmel428b8512017-08-03 13:28:28 +02004226
4227 list_add_tail(&fib6_entry->common.nexthop_group_node,
4228 &nh_grp->fib_list);
4229 fib6_entry->common.nh_group = nh_grp;
4230
4231 return 0;
4232}
4233
4234static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
4235 struct mlxsw_sp_fib_entry *fib_entry)
4236{
4237 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
4238
4239 list_del(&fib_entry->nexthop_group_node);
4240 if (!list_empty(&nh_grp->fib_list))
4241 return;
4242 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
4243}
4244
4245static int
4246mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
4247 struct mlxsw_sp_fib6_entry *fib6_entry)
4248{
4249 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
4250 int err;
4251
4252 fib6_entry->common.nh_group = NULL;
4253 list_del(&fib6_entry->common.nexthop_group_node);
4254
4255 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
4256 if (err)
4257 goto err_nexthop6_group_get;
4258
4259 /* In case this entry is offloaded, then the adjacency index
4260 * currently associated with it in the device's table is that
4261 * of the old group. Start using the new one instead.
4262 */
4263 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
4264 if (err)
4265 goto err_fib_node_entry_add;
4266
4267 if (list_empty(&old_nh_grp->fib_list))
4268 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
4269
4270 return 0;
4271
4272err_fib_node_entry_add:
4273 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
4274err_nexthop6_group_get:
4275 list_add_tail(&fib6_entry->common.nexthop_group_node,
4276 &old_nh_grp->fib_list);
4277 fib6_entry->common.nh_group = old_nh_grp;
4278 return err;
4279}
4280
4281static int
4282mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
4283 struct mlxsw_sp_fib6_entry *fib6_entry,
4284 struct rt6_info *rt)
4285{
4286 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4287 int err;
4288
4289 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
4290 if (IS_ERR(mlxsw_sp_rt6))
4291 return PTR_ERR(mlxsw_sp_rt6);
4292
4293 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
4294 fib6_entry->nrt6++;
4295
4296 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
4297 if (err)
4298 goto err_nexthop6_group_update;
4299
4300 return 0;
4301
4302err_nexthop6_group_update:
4303 fib6_entry->nrt6--;
4304 list_del(&mlxsw_sp_rt6->list);
4305 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4306 return err;
4307}
4308
4309static void
4310mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
4311 struct mlxsw_sp_fib6_entry *fib6_entry,
4312 struct rt6_info *rt)
4313{
4314 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4315
4316 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
4317 if (WARN_ON(!mlxsw_sp_rt6))
4318 return;
4319
4320 fib6_entry->nrt6--;
4321 list_del(&mlxsw_sp_rt6->list);
4322 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
4323 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4324}
4325
Petr Machataf6050ee2017-09-02 23:49:21 +02004326static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp,
4327 struct mlxsw_sp_fib_entry *fib_entry,
Ido Schimmel428b8512017-08-03 13:28:28 +02004328 const struct rt6_info *rt)
4329{
4330 /* Packets hitting RTF_REJECT routes need to be discarded by the
4331 * stack. We can rely on their destination device not having a
4332 * RIF (it's the loopback device) and can thus use action type
4333 * local, which will cause them to be trapped with a lower
4334 * priority than packets that need to be locally received.
4335 */
Ido Schimmeld3b6d372017-09-01 10:58:55 +02004336 if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel428b8512017-08-03 13:28:28 +02004337 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
4338 else if (rt->rt6i_flags & RTF_REJECT)
4339 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Petr Machataf6050ee2017-09-02 23:49:21 +02004340 else if (mlxsw_sp_rt6_is_gateway(mlxsw_sp, rt))
Ido Schimmel428b8512017-08-03 13:28:28 +02004341 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
4342 else
4343 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
4344}
4345
4346static void
4347mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
4348{
4349 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
4350
4351 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
4352 list) {
4353 fib6_entry->nrt6--;
4354 list_del(&mlxsw_sp_rt6->list);
4355 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4356 }
4357}
4358
4359static struct mlxsw_sp_fib6_entry *
4360mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
4361 struct mlxsw_sp_fib_node *fib_node,
4362 struct rt6_info *rt)
4363{
4364 struct mlxsw_sp_fib6_entry *fib6_entry;
4365 struct mlxsw_sp_fib_entry *fib_entry;
4366 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4367 int err;
4368
4369 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
4370 if (!fib6_entry)
4371 return ERR_PTR(-ENOMEM);
4372 fib_entry = &fib6_entry->common;
4373
4374 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
4375 if (IS_ERR(mlxsw_sp_rt6)) {
4376 err = PTR_ERR(mlxsw_sp_rt6);
4377 goto err_rt6_create;
4378 }
4379
Petr Machataf6050ee2017-09-02 23:49:21 +02004380 mlxsw_sp_fib6_entry_type_set(mlxsw_sp, fib_entry, mlxsw_sp_rt6->rt);
Ido Schimmel428b8512017-08-03 13:28:28 +02004381
4382 INIT_LIST_HEAD(&fib6_entry->rt6_list);
4383 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
4384 fib6_entry->nrt6 = 1;
4385 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
4386 if (err)
4387 goto err_nexthop6_group_get;
4388
4389 fib_entry->fib_node = fib_node;
4390
4391 return fib6_entry;
4392
4393err_nexthop6_group_get:
4394 list_del(&mlxsw_sp_rt6->list);
4395 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4396err_rt6_create:
4397 kfree(fib6_entry);
4398 return ERR_PTR(err);
4399}
4400
4401static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
4402 struct mlxsw_sp_fib6_entry *fib6_entry)
4403{
4404 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
4405 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
4406 WARN_ON(fib6_entry->nrt6);
4407 kfree(fib6_entry);
4408}
4409
4410static struct mlxsw_sp_fib6_entry *
4411mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004412 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004413{
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004414 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
Ido Schimmel428b8512017-08-03 13:28:28 +02004415
4416 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4417 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4418
4419 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
4420 continue;
4421 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
4422 break;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004423 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
4424 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
4425 mlxsw_sp_fib6_rt_can_mp(nrt))
4426 return fib6_entry;
4427 if (mlxsw_sp_fib6_rt_can_mp(nrt))
4428 fallback = fallback ?: fib6_entry;
4429 }
Ido Schimmel428b8512017-08-03 13:28:28 +02004430 if (rt->rt6i_metric > nrt->rt6i_metric)
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004431 return fallback ?: fib6_entry;
Ido Schimmel428b8512017-08-03 13:28:28 +02004432 }
4433
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004434 return fallback;
Ido Schimmel428b8512017-08-03 13:28:28 +02004435}
4436
4437static int
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004438mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
4439 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004440{
4441 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
4442 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
4443 struct mlxsw_sp_fib6_entry *fib6_entry;
4444
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004445 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
4446
4447 if (replace && WARN_ON(!fib6_entry))
4448 return -EINVAL;
Ido Schimmel428b8512017-08-03 13:28:28 +02004449
4450 if (fib6_entry) {
4451 list_add_tail(&new6_entry->common.list,
4452 &fib6_entry->common.list);
4453 } else {
4454 struct mlxsw_sp_fib6_entry *last;
4455
4456 list_for_each_entry(last, &fib_node->entry_list, common.list) {
4457 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
4458
4459 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
4460 break;
4461 fib6_entry = last;
4462 }
4463
4464 if (fib6_entry)
4465 list_add(&new6_entry->common.list,
4466 &fib6_entry->common.list);
4467 else
4468 list_add(&new6_entry->common.list,
4469 &fib_node->entry_list);
4470 }
4471
4472 return 0;
4473}
4474
4475static void
4476mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
4477{
4478 list_del(&fib6_entry->common.list);
4479}
4480
4481static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004482 struct mlxsw_sp_fib6_entry *fib6_entry,
4483 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004484{
4485 int err;
4486
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004487 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004488 if (err)
4489 return err;
4490
4491 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
4492 if (err)
4493 goto err_fib_node_entry_add;
4494
4495 return 0;
4496
4497err_fib_node_entry_add:
4498 mlxsw_sp_fib6_node_list_remove(fib6_entry);
4499 return err;
4500}
4501
4502static void
4503mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
4504 struct mlxsw_sp_fib6_entry *fib6_entry)
4505{
4506 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
4507 mlxsw_sp_fib6_node_list_remove(fib6_entry);
4508}
4509
4510static struct mlxsw_sp_fib6_entry *
4511mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
4512 const struct rt6_info *rt)
4513{
4514 struct mlxsw_sp_fib6_entry *fib6_entry;
4515 struct mlxsw_sp_fib_node *fib_node;
4516 struct mlxsw_sp_fib *fib;
4517 struct mlxsw_sp_vr *vr;
4518
4519 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
4520 if (!vr)
4521 return NULL;
4522 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
4523
4524 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
4525 sizeof(rt->rt6i_dst.addr),
4526 rt->rt6i_dst.plen);
4527 if (!fib_node)
4528 return NULL;
4529
4530 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4531 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4532
4533 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
4534 rt->rt6i_metric == iter_rt->rt6i_metric &&
4535 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
4536 return fib6_entry;
4537 }
4538
4539 return NULL;
4540}
4541
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004542static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
4543 struct mlxsw_sp_fib6_entry *fib6_entry,
4544 bool replace)
4545{
4546 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
4547 struct mlxsw_sp_fib6_entry *replaced;
4548
4549 if (!replace)
4550 return;
4551
4552 replaced = list_next_entry(fib6_entry, common.list);
4553
4554 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
4555 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
4556 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4557}
4558
Ido Schimmel428b8512017-08-03 13:28:28 +02004559static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004560 struct rt6_info *rt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004561{
4562 struct mlxsw_sp_fib6_entry *fib6_entry;
4563 struct mlxsw_sp_fib_node *fib_node;
4564 int err;
4565
4566 if (mlxsw_sp->router->aborted)
4567 return 0;
4568
Ido Schimmelf36f5ac2017-08-03 13:28:30 +02004569 if (rt->rt6i_src.plen)
4570 return -EINVAL;
4571
Ido Schimmel428b8512017-08-03 13:28:28 +02004572 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4573 return 0;
4574
4575 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
4576 &rt->rt6i_dst.addr,
4577 sizeof(rt->rt6i_dst.addr),
4578 rt->rt6i_dst.plen,
4579 MLXSW_SP_L3_PROTO_IPV6);
4580 if (IS_ERR(fib_node))
4581 return PTR_ERR(fib_node);
4582
4583 /* Before creating a new entry, try to append route to an existing
4584 * multipath entry.
4585 */
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004586 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004587 if (fib6_entry) {
4588 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
4589 if (err)
4590 goto err_fib6_entry_nexthop_add;
4591 return 0;
4592 }
4593
4594 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
4595 if (IS_ERR(fib6_entry)) {
4596 err = PTR_ERR(fib6_entry);
4597 goto err_fib6_entry_create;
4598 }
4599
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004600 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004601 if (err)
4602 goto err_fib6_node_entry_link;
4603
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004604 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
4605
Ido Schimmel428b8512017-08-03 13:28:28 +02004606 return 0;
4607
4608err_fib6_node_entry_link:
4609 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4610err_fib6_entry_create:
4611err_fib6_entry_nexthop_add:
4612 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4613 return err;
4614}
4615
4616static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
4617 struct rt6_info *rt)
4618{
4619 struct mlxsw_sp_fib6_entry *fib6_entry;
4620 struct mlxsw_sp_fib_node *fib_node;
4621
4622 if (mlxsw_sp->router->aborted)
4623 return;
4624
4625 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4626 return;
4627
4628 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
4629 if (WARN_ON(!fib6_entry))
4630 return;
4631
4632 /* If route is part of a multipath entry, but not the last one
4633 * removed, then only reduce its nexthop group.
4634 */
4635 if (!list_is_singular(&fib6_entry->rt6_list)) {
4636 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
4637 return;
4638 }
4639
4640 fib_node = fib6_entry->common.fib_node;
4641
4642 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4643 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4644 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4645}
4646
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004647static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
4648 enum mlxsw_reg_ralxx_protocol proto,
4649 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004650{
4651 char ralta_pl[MLXSW_REG_RALTA_LEN];
4652 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004653 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004654
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004655 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004656 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
4657 if (err)
4658 return err;
4659
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004660 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004661 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
4662 if (err)
4663 return err;
4664
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004665 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004666 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004667 char raltb_pl[MLXSW_REG_RALTB_LEN];
4668 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004669
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004670 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004671 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
4672 raltb_pl);
4673 if (err)
4674 return err;
4675
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004676 mlxsw_reg_ralue_pack(ralue_pl, proto,
4677 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004678 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
4679 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
4680 ralue_pl);
4681 if (err)
4682 return err;
4683 }
4684
4685 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004686}
4687
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004688static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
4689{
4690 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
4691 int err;
4692
4693 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4694 MLXSW_SP_LPM_TREE_MIN);
4695 if (err)
4696 return err;
4697
4698 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
4699 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4700 MLXSW_SP_LPM_TREE_MIN + 1);
4701}
4702
Ido Schimmel9aecce12017-02-09 10:28:42 +01004703static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
4704 struct mlxsw_sp_fib_node *fib_node)
4705{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004706 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004707
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004708 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
4709 common.list) {
4710 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004711
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004712 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
4713 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02004714 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004715 /* Break when entry list is empty and node was freed.
4716 * Otherwise, we'll access freed memory in the next
4717 * iteration.
4718 */
4719 if (do_break)
4720 break;
4721 }
4722}
4723
Ido Schimmel428b8512017-08-03 13:28:28 +02004724static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
4725 struct mlxsw_sp_fib_node *fib_node)
4726{
4727 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
4728
4729 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
4730 common.list) {
4731 bool do_break = &tmp->common.list == &fib_node->entry_list;
4732
4733 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4734 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4735 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4736 if (do_break)
4737 break;
4738 }
4739}
4740
Ido Schimmel9aecce12017-02-09 10:28:42 +01004741static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
4742 struct mlxsw_sp_fib_node *fib_node)
4743{
Ido Schimmel76610eb2017-03-10 08:53:41 +01004744 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01004745 case MLXSW_SP_L3_PROTO_IPV4:
4746 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
4747 break;
4748 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02004749 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004750 break;
4751 }
4752}
4753
Ido Schimmel76610eb2017-03-10 08:53:41 +01004754static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
4755 struct mlxsw_sp_vr *vr,
4756 enum mlxsw_sp_l3proto proto)
4757{
4758 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
4759 struct mlxsw_sp_fib_node *fib_node, *tmp;
4760
4761 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
4762 bool do_break = &tmp->list == &fib->node_list;
4763
4764 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
4765 if (do_break)
4766 break;
4767 }
4768}
4769
Ido Schimmelac571de2016-11-14 11:26:32 +01004770static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004771{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004772 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004773
Jiri Pirkoc1a38312016-10-21 16:07:23 +02004774 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004775 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01004776
Ido Schimmel76610eb2017-03-10 08:53:41 +01004777 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004778 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01004779 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02004780
4781 /* If virtual router was only used for IPv4, then it's no
4782 * longer used.
4783 */
4784 if (!mlxsw_sp_vr_is_used(vr))
4785 continue;
4786 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004787 }
Ido Schimmelac571de2016-11-14 11:26:32 +01004788}
4789
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004790static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01004791{
4792 int err;
4793
Ido Schimmel9011b672017-05-16 19:38:25 +02004794 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01004795 return;
4796 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 +01004797 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004798 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004799 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
4800 if (err)
4801 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
4802}
4803
Ido Schimmel30572242016-12-03 16:45:01 +01004804struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01004805 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01004806 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02004807 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004808 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004809 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004810 struct fib_nh_notifier_info fnh_info;
4811 };
Ido Schimmel30572242016-12-03 16:45:01 +01004812 struct mlxsw_sp *mlxsw_sp;
4813 unsigned long event;
4814};
4815
Ido Schimmel66a57632017-08-03 13:28:26 +02004816static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004817{
Ido Schimmel30572242016-12-03 16:45:01 +01004818 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01004819 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01004820 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004821 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004822 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004823 int err;
4824
Ido Schimmel30572242016-12-03 16:45:01 +01004825 /* Protect internal structures from changes */
4826 rtnl_lock();
4827 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004828 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01004829 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004830 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004831 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01004832 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
4833 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004834 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004835 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004836 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01004837 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004838 break;
4839 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01004840 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
4841 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004842 break;
4843 case FIB_EVENT_RULE_ADD: /* fall through */
4844 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004845 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01004846 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004847 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004848 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004849 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01004850 case FIB_EVENT_NH_ADD: /* fall through */
4851 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02004852 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
4853 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01004854 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
4855 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004856 }
Ido Schimmel30572242016-12-03 16:45:01 +01004857 rtnl_unlock();
4858 kfree(fib_work);
4859}
4860
Ido Schimmel66a57632017-08-03 13:28:26 +02004861static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
4862{
Ido Schimmel583419f2017-08-03 13:28:27 +02004863 struct mlxsw_sp_fib_event_work *fib_work =
4864 container_of(work, struct mlxsw_sp_fib_event_work, work);
4865 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
4866 struct fib_rule *rule;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004867 bool replace;
Ido Schimmel428b8512017-08-03 13:28:28 +02004868 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02004869
4870 rtnl_lock();
4871 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004872 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004873 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004874 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel428b8512017-08-03 13:28:28 +02004875 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004876 fib_work->fen6_info.rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004877 if (err)
4878 mlxsw_sp_router_fib_abort(mlxsw_sp);
4879 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4880 break;
4881 case FIB_EVENT_ENTRY_DEL:
4882 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
4883 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4884 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004885 case FIB_EVENT_RULE_ADD: /* fall through */
4886 case FIB_EVENT_RULE_DEL:
4887 rule = fib_work->fr_info.rule;
4888 if (!fib6_rule_default(rule) && !rule->l3mdev)
4889 mlxsw_sp_router_fib_abort(mlxsw_sp);
4890 fib_rule_put(rule);
4891 break;
4892 }
4893 rtnl_unlock();
4894 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02004895}
4896
4897static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
4898 struct fib_notifier_info *info)
4899{
4900 switch (fib_work->event) {
4901 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4902 case FIB_EVENT_ENTRY_APPEND: /* fall through */
4903 case FIB_EVENT_ENTRY_ADD: /* fall through */
4904 case FIB_EVENT_ENTRY_DEL:
4905 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
4906 /* Take referece on fib_info to prevent it from being
4907 * freed while work is queued. Release it afterwards.
4908 */
4909 fib_info_hold(fib_work->fen_info.fi);
4910 break;
4911 case FIB_EVENT_RULE_ADD: /* fall through */
4912 case FIB_EVENT_RULE_DEL:
4913 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4914 fib_rule_get(fib_work->fr_info.rule);
4915 break;
4916 case FIB_EVENT_NH_ADD: /* fall through */
4917 case FIB_EVENT_NH_DEL:
4918 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
4919 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
4920 break;
4921 }
4922}
4923
4924static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
4925 struct fib_notifier_info *info)
4926{
Ido Schimmel583419f2017-08-03 13:28:27 +02004927 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004928 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004929 case FIB_EVENT_ENTRY_ADD: /* fall through */
4930 case FIB_EVENT_ENTRY_DEL:
4931 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
4932 rt6_hold(fib_work->fen6_info.rt);
4933 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004934 case FIB_EVENT_RULE_ADD: /* fall through */
4935 case FIB_EVENT_RULE_DEL:
4936 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4937 fib_rule_get(fib_work->fr_info.rule);
4938 break;
4939 }
Ido Schimmel66a57632017-08-03 13:28:26 +02004940}
4941
Ido Schimmel30572242016-12-03 16:45:01 +01004942/* Called with rcu_read_lock() */
4943static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
4944 unsigned long event, void *ptr)
4945{
Ido Schimmel30572242016-12-03 16:45:01 +01004946 struct mlxsw_sp_fib_event_work *fib_work;
4947 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02004948 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01004949
Ido Schimmel8e29f972017-09-15 15:31:07 +02004950 if (!net_eq(info->net, &init_net) ||
4951 (info->family != AF_INET && info->family != AF_INET6))
Ido Schimmel30572242016-12-03 16:45:01 +01004952 return NOTIFY_DONE;
4953
4954 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
4955 if (WARN_ON(!fib_work))
4956 return NOTIFY_BAD;
4957
Ido Schimmel7e39d112017-05-16 19:38:28 +02004958 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4959 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01004960 fib_work->event = event;
4961
Ido Schimmel66a57632017-08-03 13:28:26 +02004962 switch (info->family) {
4963 case AF_INET:
4964 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
4965 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01004966 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02004967 case AF_INET6:
4968 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
4969 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01004970 break;
Ido Schimmel30572242016-12-03 16:45:01 +01004971 }
4972
Ido Schimmela0e47612017-02-06 16:20:10 +01004973 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01004974
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004975 return NOTIFY_DONE;
4976}
4977
Ido Schimmel4724ba562017-03-10 08:53:39 +01004978static struct mlxsw_sp_rif *
4979mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
4980 const struct net_device *dev)
4981{
4982 int i;
4983
4984 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004985 if (mlxsw_sp->router->rifs[i] &&
4986 mlxsw_sp->router->rifs[i]->dev == dev)
4987 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004988
4989 return NULL;
4990}
4991
4992static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
4993{
4994 char ritr_pl[MLXSW_REG_RITR_LEN];
4995 int err;
4996
4997 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
4998 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4999 if (WARN_ON_ONCE(err))
5000 return err;
5001
5002 mlxsw_reg_ritr_enable_set(ritr_pl, false);
5003 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5004}
5005
5006static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005007 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005008{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005009 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
5010 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
5011 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005012}
5013
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005014static bool
5015mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
5016 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005017{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005018 struct inet6_dev *inet6_dev;
5019 bool addr_list_empty = true;
5020 struct in_device *idev;
5021
Ido Schimmel4724ba562017-03-10 08:53:39 +01005022 switch (event) {
5023 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02005024 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005025 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005026 idev = __in_dev_get_rtnl(dev);
5027 if (idev && idev->ifa_list)
5028 addr_list_empty = false;
5029
5030 inet6_dev = __in6_dev_get(dev);
5031 if (addr_list_empty && inet6_dev &&
5032 !list_empty(&inet6_dev->addr_list))
5033 addr_list_empty = false;
5034
5035 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005036 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01005037 return true;
5038 /* It is possible we already removed the RIF ourselves
5039 * if it was assigned to a netdev that is now a bridge
5040 * or LAG slave.
5041 */
5042 return false;
5043 }
5044
5045 return false;
5046}
5047
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005048static enum mlxsw_sp_rif_type
5049mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
5050 const struct net_device *dev)
5051{
5052 enum mlxsw_sp_fid_type type;
5053
Petr Machata6ddb7422017-09-02 23:49:19 +02005054 if (mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL))
5055 return MLXSW_SP_RIF_TYPE_IPIP_LB;
5056
5057 /* Otherwise RIF type is derived from the type of the underlying FID. */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005058 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
5059 type = MLXSW_SP_FID_TYPE_8021Q;
5060 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
5061 type = MLXSW_SP_FID_TYPE_8021Q;
5062 else if (netif_is_bridge_master(dev))
5063 type = MLXSW_SP_FID_TYPE_8021D;
5064 else
5065 type = MLXSW_SP_FID_TYPE_RFID;
5066
5067 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
5068}
5069
Ido Schimmelde5ed992017-06-04 16:53:40 +02005070static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005071{
5072 int i;
5073
Ido Schimmelde5ed992017-06-04 16:53:40 +02005074 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
5075 if (!mlxsw_sp->router->rifs[i]) {
5076 *p_rif_index = i;
5077 return 0;
5078 }
5079 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01005080
Ido Schimmelde5ed992017-06-04 16:53:40 +02005081 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005082}
5083
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005084static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
5085 u16 vr_id,
5086 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005087{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005088 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005089
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005090 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005091 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005092 return NULL;
5093
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005094 INIT_LIST_HEAD(&rif->nexthop_list);
5095 INIT_LIST_HEAD(&rif->neigh_list);
5096 ether_addr_copy(rif->addr, l3_dev->dev_addr);
5097 rif->mtu = l3_dev->mtu;
5098 rif->vr_id = vr_id;
5099 rif->dev = l3_dev;
5100 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005101
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005102 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005103}
5104
Ido Schimmel5f9efff2017-05-16 19:38:27 +02005105struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
5106 u16 rif_index)
5107{
5108 return mlxsw_sp->router->rifs[rif_index];
5109}
5110
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02005111u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
5112{
5113 return rif->rif_index;
5114}
5115
Petr Machata92107cf2017-09-02 23:49:28 +02005116u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
5117{
5118 return lb_rif->common.rif_index;
5119}
5120
5121u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
5122{
5123 return lb_rif->ul_vr_id;
5124}
5125
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02005126int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
5127{
5128 return rif->dev->ifindex;
5129}
5130
Yotam Gigi91e4d592017-09-19 10:00:19 +02005131const struct net_device *mlxsw_sp_rif_dev(const struct mlxsw_sp_rif *rif)
5132{
5133 return rif->dev;
5134}
5135
Ido Schimmel4724ba562017-03-10 08:53:39 +01005136static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005137mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
5138 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005139{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005140 u32 tb_id = l3mdev_fib_table(params->dev);
5141 const struct mlxsw_sp_rif_ops *ops;
Petr Machata010cadf2017-09-02 23:49:18 +02005142 struct mlxsw_sp_fid *fid = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005143 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005144 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005145 struct mlxsw_sp_vr *vr;
5146 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005147 int err;
5148
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005149 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
5150 ops = mlxsw_sp->router->rif_ops_arr[type];
5151
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005152 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
5153 if (IS_ERR(vr))
5154 return ERR_CAST(vr);
5155
Ido Schimmelde5ed992017-06-04 16:53:40 +02005156 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
5157 if (err)
5158 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005159
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005160 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02005161 if (!rif) {
5162 err = -ENOMEM;
5163 goto err_rif_alloc;
5164 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005165 rif->mlxsw_sp = mlxsw_sp;
5166 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02005167
Petr Machata010cadf2017-09-02 23:49:18 +02005168 if (ops->fid_get) {
5169 fid = ops->fid_get(rif);
5170 if (IS_ERR(fid)) {
5171 err = PTR_ERR(fid);
5172 goto err_fid_get;
5173 }
5174 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02005175 }
5176
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005177 if (ops->setup)
5178 ops->setup(rif, params);
5179
5180 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005181 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005182 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005183
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005184 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02005185 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel69132292017-03-10 08:53:42 +01005186 vr->rif_count++;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005187
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005188 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005189
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005190err_configure:
Petr Machata010cadf2017-09-02 23:49:18 +02005191 if (fid)
5192 mlxsw_sp_fid_put(fid);
Ido Schimmela1107482017-05-26 08:37:39 +02005193err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005194 kfree(rif);
5195err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02005196err_rif_index_alloc:
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005197 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005198 return ERR_PTR(err);
5199}
5200
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005201void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005202{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005203 const struct mlxsw_sp_rif_ops *ops = rif->ops;
5204 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02005205 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005206 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005207
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005208 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005209 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02005210
Ido Schimmel69132292017-03-10 08:53:42 +01005211 vr->rif_count--;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005212 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005213 mlxsw_sp_rif_counters_free(rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005214 ops->deconfigure(rif);
Petr Machata010cadf2017-09-02 23:49:18 +02005215 if (fid)
5216 /* Loopback RIFs are not associated with a FID. */
5217 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005218 kfree(rif);
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005219 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005220}
5221
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005222static void
5223mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
5224 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
5225{
5226 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
5227
5228 params->vid = mlxsw_sp_port_vlan->vid;
5229 params->lag = mlxsw_sp_port->lagged;
5230 if (params->lag)
5231 params->lag_id = mlxsw_sp_port->lag_id;
5232 else
5233 params->system_port = mlxsw_sp_port->local_port;
5234}
5235
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005236static int
Ido Schimmela1107482017-05-26 08:37:39 +02005237mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005238 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005239{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005240 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02005241 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005242 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005243 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005244 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005245 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005246
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02005247 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005248 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005249 struct mlxsw_sp_rif_params params = {
5250 .dev = l3_dev,
5251 };
5252
5253 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
5254 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005255 if (IS_ERR(rif))
5256 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005257 }
5258
Ido Schimmela1107482017-05-26 08:37:39 +02005259 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005260 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02005261 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
5262 if (err)
5263 goto err_fid_port_vid_map;
5264
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005265 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005266 if (err)
5267 goto err_port_vid_learning_set;
5268
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005269 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005270 BR_STATE_FORWARDING);
5271 if (err)
5272 goto err_port_vid_stp_set;
5273
Ido Schimmela1107482017-05-26 08:37:39 +02005274 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005275
Ido Schimmel4724ba562017-03-10 08:53:39 +01005276 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005277
5278err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005279 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005280err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02005281 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
5282err_fid_port_vid_map:
5283 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005284 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005285}
5286
Ido Schimmela1107482017-05-26 08:37:39 +02005287void
5288mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005289{
Ido Schimmelce95e152017-05-26 08:37:27 +02005290 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005291 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02005292 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02005293
Ido Schimmela1107482017-05-26 08:37:39 +02005294 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
5295 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02005296
Ido Schimmela1107482017-05-26 08:37:39 +02005297 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005298 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
5299 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02005300 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
5301 /* If router port holds the last reference on the rFID, then the
5302 * associated Sub-port RIF will be destroyed.
5303 */
5304 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005305}
5306
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005307static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
5308 struct net_device *port_dev,
5309 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005310{
5311 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02005312 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005313
Ido Schimmelce95e152017-05-26 08:37:27 +02005314 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005315 if (WARN_ON(!mlxsw_sp_port_vlan))
5316 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005317
5318 switch (event) {
5319 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02005320 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005321 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005322 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02005323 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005324 break;
5325 }
5326
5327 return 0;
5328}
5329
5330static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
5331 unsigned long event)
5332{
Jiri Pirko2b94e582017-04-18 16:55:37 +02005333 if (netif_is_bridge_port(port_dev) ||
5334 netif_is_lag_port(port_dev) ||
5335 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01005336 return 0;
5337
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005338 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005339}
5340
5341static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
5342 struct net_device *lag_dev,
5343 unsigned long event, u16 vid)
5344{
5345 struct net_device *port_dev;
5346 struct list_head *iter;
5347 int err;
5348
5349 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
5350 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005351 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
5352 port_dev,
5353 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005354 if (err)
5355 return err;
5356 }
5357 }
5358
5359 return 0;
5360}
5361
5362static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
5363 unsigned long event)
5364{
5365 if (netif_is_bridge_port(lag_dev))
5366 return 0;
5367
5368 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
5369}
5370
Ido Schimmel4724ba562017-03-10 08:53:39 +01005371static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01005372 unsigned long event)
5373{
5374 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005375 struct mlxsw_sp_rif_params params = {
5376 .dev = l3_dev,
5377 };
Ido Schimmela1107482017-05-26 08:37:39 +02005378 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005379
5380 switch (event) {
5381 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005382 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
5383 if (IS_ERR(rif))
5384 return PTR_ERR(rif);
5385 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005386 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02005387 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005388 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005389 break;
5390 }
5391
5392 return 0;
5393}
5394
5395static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
5396 unsigned long event)
5397{
5398 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005399 u16 vid = vlan_dev_vlan_id(vlan_dev);
5400
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03005401 if (netif_is_bridge_port(vlan_dev))
5402 return 0;
5403
Ido Schimmel4724ba562017-03-10 08:53:39 +01005404 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005405 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
5406 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005407 else if (netif_is_lag_master(real_dev))
5408 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
5409 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02005410 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02005411 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005412
5413 return 0;
5414}
5415
Ido Schimmelb1e45522017-04-30 19:47:14 +03005416static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
5417 unsigned long event)
5418{
5419 if (mlxsw_sp_port_dev_check(dev))
5420 return mlxsw_sp_inetaddr_port_event(dev, event);
5421 else if (netif_is_lag_master(dev))
5422 return mlxsw_sp_inetaddr_lag_event(dev, event);
5423 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02005424 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03005425 else if (is_vlan_dev(dev))
5426 return mlxsw_sp_inetaddr_vlan_event(dev, event);
5427 else
5428 return 0;
5429}
5430
Ido Schimmel4724ba562017-03-10 08:53:39 +01005431int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
5432 unsigned long event, void *ptr)
5433{
5434 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
5435 struct net_device *dev = ifa->ifa_dev->dev;
5436 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005437 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005438 int err = 0;
5439
5440 mlxsw_sp = mlxsw_sp_lower_get(dev);
5441 if (!mlxsw_sp)
5442 goto out;
5443
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005444 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005445 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01005446 goto out;
5447
Ido Schimmelb1e45522017-04-30 19:47:14 +03005448 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005449out:
5450 return notifier_from_errno(err);
5451}
5452
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005453struct mlxsw_sp_inet6addr_event_work {
5454 struct work_struct work;
5455 struct net_device *dev;
5456 unsigned long event;
5457};
5458
5459static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
5460{
5461 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
5462 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
5463 struct net_device *dev = inet6addr_work->dev;
5464 unsigned long event = inet6addr_work->event;
5465 struct mlxsw_sp *mlxsw_sp;
5466 struct mlxsw_sp_rif *rif;
5467
5468 rtnl_lock();
5469 mlxsw_sp = mlxsw_sp_lower_get(dev);
5470 if (!mlxsw_sp)
5471 goto out;
5472
5473 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5474 if (!mlxsw_sp_rif_should_config(rif, dev, event))
5475 goto out;
5476
5477 __mlxsw_sp_inetaddr_event(dev, event);
5478out:
5479 rtnl_unlock();
5480 dev_put(dev);
5481 kfree(inet6addr_work);
5482}
5483
5484/* Called with rcu_read_lock() */
5485int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
5486 unsigned long event, void *ptr)
5487{
5488 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
5489 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
5490 struct net_device *dev = if6->idev->dev;
5491
5492 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
5493 return NOTIFY_DONE;
5494
5495 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
5496 if (!inet6addr_work)
5497 return NOTIFY_BAD;
5498
5499 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
5500 inet6addr_work->dev = dev;
5501 inet6addr_work->event = event;
5502 dev_hold(dev);
5503 mlxsw_core_schedule_work(&inet6addr_work->work);
5504
5505 return NOTIFY_DONE;
5506}
5507
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005508static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01005509 const char *mac, int mtu)
5510{
5511 char ritr_pl[MLXSW_REG_RITR_LEN];
5512 int err;
5513
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005514 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005515 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5516 if (err)
5517 return err;
5518
5519 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
5520 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
5521 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
5522 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5523}
5524
5525int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
5526{
5527 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005528 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005529 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005530 int err;
5531
5532 mlxsw_sp = mlxsw_sp_lower_get(dev);
5533 if (!mlxsw_sp)
5534 return 0;
5535
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005536 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5537 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005538 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02005539 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005540
Ido Schimmela1107482017-05-26 08:37:39 +02005541 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005542 if (err)
5543 return err;
5544
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005545 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
5546 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005547 if (err)
5548 goto err_rif_edit;
5549
Ido Schimmela1107482017-05-26 08:37:39 +02005550 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005551 if (err)
5552 goto err_rif_fdb_op;
5553
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005554 ether_addr_copy(rif->addr, dev->dev_addr);
5555 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005556
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005557 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005558
5559 return 0;
5560
5561err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005562 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005563err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02005564 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005565 return err;
5566}
5567
Ido Schimmelb1e45522017-04-30 19:47:14 +03005568static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
5569 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005570{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005571 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01005572
Ido Schimmelb1e45522017-04-30 19:47:14 +03005573 /* If netdev is already associated with a RIF, then we need to
5574 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01005575 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03005576 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
5577 if (rif)
5578 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005579
Ido Schimmelb1e45522017-04-30 19:47:14 +03005580 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005581}
5582
Ido Schimmelb1e45522017-04-30 19:47:14 +03005583static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
5584 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005585{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005586 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01005587
Ido Schimmelb1e45522017-04-30 19:47:14 +03005588 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
5589 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005590 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03005591 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005592}
5593
Ido Schimmelb1e45522017-04-30 19:47:14 +03005594int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
5595 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005596{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005597 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
5598 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005599
Ido Schimmelb1e45522017-04-30 19:47:14 +03005600 if (!mlxsw_sp)
5601 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005602
Ido Schimmelb1e45522017-04-30 19:47:14 +03005603 switch (event) {
5604 case NETDEV_PRECHANGEUPPER:
5605 return 0;
5606 case NETDEV_CHANGEUPPER:
5607 if (info->linking)
5608 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
5609 else
5610 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
5611 break;
5612 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005613
Ido Schimmelb1e45522017-04-30 19:47:14 +03005614 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005615}
5616
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005617static struct mlxsw_sp_rif_subport *
5618mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02005619{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005620 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02005621}
5622
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005623static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
5624 const struct mlxsw_sp_rif_params *params)
5625{
5626 struct mlxsw_sp_rif_subport *rif_subport;
5627
5628 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5629 rif_subport->vid = params->vid;
5630 rif_subport->lag = params->lag;
5631 if (params->lag)
5632 rif_subport->lag_id = params->lag_id;
5633 else
5634 rif_subport->system_port = params->system_port;
5635}
5636
5637static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
5638{
5639 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5640 struct mlxsw_sp_rif_subport *rif_subport;
5641 char ritr_pl[MLXSW_REG_RITR_LEN];
5642
5643 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5644 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
Petr Machata9571e822017-09-02 23:49:14 +02005645 rif->rif_index, rif->vr_id, rif->dev->mtu);
5646 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005647 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
5648 rif_subport->lag ? rif_subport->lag_id :
5649 rif_subport->system_port,
5650 rif_subport->vid);
5651
5652 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5653}
5654
5655static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
5656{
Petr Machata010cadf2017-09-02 23:49:18 +02005657 int err;
5658
5659 err = mlxsw_sp_rif_subport_op(rif, true);
5660 if (err)
5661 return err;
5662
5663 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5664 mlxsw_sp_fid_index(rif->fid), true);
5665 if (err)
5666 goto err_rif_fdb_op;
5667
5668 mlxsw_sp_fid_rif_set(rif->fid, rif);
5669 return 0;
5670
5671err_rif_fdb_op:
5672 mlxsw_sp_rif_subport_op(rif, false);
5673 return err;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005674}
5675
5676static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
5677{
Petr Machata010cadf2017-09-02 23:49:18 +02005678 struct mlxsw_sp_fid *fid = rif->fid;
5679
5680 mlxsw_sp_fid_rif_set(fid, NULL);
5681 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5682 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005683 mlxsw_sp_rif_subport_op(rif, false);
5684}
5685
5686static struct mlxsw_sp_fid *
5687mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
5688{
5689 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
5690}
5691
5692static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
5693 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
5694 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
5695 .setup = mlxsw_sp_rif_subport_setup,
5696 .configure = mlxsw_sp_rif_subport_configure,
5697 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
5698 .fid_get = mlxsw_sp_rif_subport_fid_get,
5699};
5700
5701static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
5702 enum mlxsw_reg_ritr_if_type type,
5703 u16 vid_fid, bool enable)
5704{
5705 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5706 char ritr_pl[MLXSW_REG_RITR_LEN];
5707
5708 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
Petr Machata9571e822017-09-02 23:49:14 +02005709 rif->dev->mtu);
5710 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005711 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
5712
5713 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5714}
5715
5716static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
5717{
5718 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
5719}
5720
5721static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
5722{
5723 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5724 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
5725 int err;
5726
5727 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
5728 if (err)
5729 return err;
5730
Ido Schimmel0d284812017-07-18 10:10:12 +02005731 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5732 mlxsw_sp_router_port(mlxsw_sp), true);
5733 if (err)
5734 goto err_fid_mc_flood_set;
5735
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005736 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5737 mlxsw_sp_router_port(mlxsw_sp), true);
5738 if (err)
5739 goto err_fid_bc_flood_set;
5740
Petr Machata010cadf2017-09-02 23:49:18 +02005741 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5742 mlxsw_sp_fid_index(rif->fid), true);
5743 if (err)
5744 goto err_rif_fdb_op;
5745
5746 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005747 return 0;
5748
Petr Machata010cadf2017-09-02 23:49:18 +02005749err_rif_fdb_op:
5750 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5751 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005752err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005753 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5754 mlxsw_sp_router_port(mlxsw_sp), false);
5755err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005756 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5757 return err;
5758}
5759
5760static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
5761{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005762 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02005763 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5764 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005765
Petr Machata010cadf2017-09-02 23:49:18 +02005766 mlxsw_sp_fid_rif_set(fid, NULL);
5767 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5768 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005769 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5770 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005771 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5772 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005773 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5774}
5775
5776static struct mlxsw_sp_fid *
5777mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
5778{
5779 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
5780
5781 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
5782}
5783
5784static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
5785 .type = MLXSW_SP_RIF_TYPE_VLAN,
5786 .rif_size = sizeof(struct mlxsw_sp_rif),
5787 .configure = mlxsw_sp_rif_vlan_configure,
5788 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
5789 .fid_get = mlxsw_sp_rif_vlan_fid_get,
5790};
5791
5792static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
5793{
5794 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5795 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
5796 int err;
5797
5798 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
5799 true);
5800 if (err)
5801 return err;
5802
Ido Schimmel0d284812017-07-18 10:10:12 +02005803 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5804 mlxsw_sp_router_port(mlxsw_sp), true);
5805 if (err)
5806 goto err_fid_mc_flood_set;
5807
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005808 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5809 mlxsw_sp_router_port(mlxsw_sp), true);
5810 if (err)
5811 goto err_fid_bc_flood_set;
5812
Petr Machata010cadf2017-09-02 23:49:18 +02005813 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5814 mlxsw_sp_fid_index(rif->fid), true);
5815 if (err)
5816 goto err_rif_fdb_op;
5817
5818 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005819 return 0;
5820
Petr Machata010cadf2017-09-02 23:49:18 +02005821err_rif_fdb_op:
5822 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5823 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005824err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005825 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5826 mlxsw_sp_router_port(mlxsw_sp), false);
5827err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005828 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5829 return err;
5830}
5831
5832static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
5833{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005834 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02005835 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5836 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005837
Petr Machata010cadf2017-09-02 23:49:18 +02005838 mlxsw_sp_fid_rif_set(fid, NULL);
5839 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5840 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005841 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5842 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005843 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5844 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005845 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5846}
5847
5848static struct mlxsw_sp_fid *
5849mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
5850{
5851 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
5852}
5853
5854static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
5855 .type = MLXSW_SP_RIF_TYPE_FID,
5856 .rif_size = sizeof(struct mlxsw_sp_rif),
5857 .configure = mlxsw_sp_rif_fid_configure,
5858 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
5859 .fid_get = mlxsw_sp_rif_fid_fid_get,
5860};
5861
Petr Machata6ddb7422017-09-02 23:49:19 +02005862static struct mlxsw_sp_rif_ipip_lb *
5863mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif)
5864{
5865 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
5866}
5867
5868static void
5869mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif,
5870 const struct mlxsw_sp_rif_params *params)
5871{
5872 struct mlxsw_sp_rif_params_ipip_lb *params_lb;
5873 struct mlxsw_sp_rif_ipip_lb *rif_lb;
5874
5875 params_lb = container_of(params, struct mlxsw_sp_rif_params_ipip_lb,
5876 common);
5877 rif_lb = mlxsw_sp_rif_ipip_lb_rif(rif);
5878 rif_lb->lb_config = params_lb->lb_config;
5879}
5880
5881static int
5882mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif,
5883 struct mlxsw_sp_vr *ul_vr, bool enable)
5884{
5885 struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config;
5886 struct mlxsw_sp_rif *rif = &lb_rif->common;
5887 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5888 char ritr_pl[MLXSW_REG_RITR_LEN];
5889 u32 saddr4;
5890
5891 switch (lb_cf.ul_protocol) {
5892 case MLXSW_SP_L3_PROTO_IPV4:
5893 saddr4 = be32_to_cpu(lb_cf.saddr.addr4);
5894 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,
5895 rif->rif_index, rif->vr_id, rif->dev->mtu);
5896 mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt,
5897 MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET,
5898 ul_vr->id, saddr4, lb_cf.okey);
5899 break;
5900
5901 case MLXSW_SP_L3_PROTO_IPV6:
5902 return -EAFNOSUPPORT;
5903 }
5904
5905 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5906}
5907
5908static int
5909mlxsw_sp_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif)
5910{
5911 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
5912 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev);
5913 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5914 struct mlxsw_sp_vr *ul_vr;
5915 int err;
5916
5917 ul_vr = mlxsw_sp_vr_get(mlxsw_sp, ul_tb_id);
5918 if (IS_ERR(ul_vr))
5919 return PTR_ERR(ul_vr);
5920
5921 err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, true);
5922 if (err)
5923 goto err_loopback_op;
5924
5925 lb_rif->ul_vr_id = ul_vr->id;
5926 ++ul_vr->rif_count;
5927 return 0;
5928
5929err_loopback_op:
5930 mlxsw_sp_vr_put(ul_vr);
5931 return err;
5932}
5933
5934static void mlxsw_sp_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif)
5935{
5936 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
5937 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5938 struct mlxsw_sp_vr *ul_vr;
5939
5940 ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id];
5941 mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, false);
5942
5943 --ul_vr->rif_count;
5944 mlxsw_sp_vr_put(ul_vr);
5945}
5946
5947static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = {
5948 .type = MLXSW_SP_RIF_TYPE_IPIP_LB,
5949 .rif_size = sizeof(struct mlxsw_sp_rif_ipip_lb),
5950 .setup = mlxsw_sp_rif_ipip_lb_setup,
5951 .configure = mlxsw_sp_rif_ipip_lb_configure,
5952 .deconfigure = mlxsw_sp_rif_ipip_lb_deconfigure,
5953};
5954
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005955static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
5956 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
5957 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
5958 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
Petr Machata6ddb7422017-09-02 23:49:19 +02005959 [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp_rif_ipip_lb_ops,
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005960};
5961
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005962static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
5963{
5964 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
5965
5966 mlxsw_sp->router->rifs = kcalloc(max_rifs,
5967 sizeof(struct mlxsw_sp_rif *),
5968 GFP_KERNEL);
5969 if (!mlxsw_sp->router->rifs)
5970 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005971
5972 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
5973
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005974 return 0;
5975}
5976
5977static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
5978{
5979 int i;
5980
5981 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
5982 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
5983
5984 kfree(mlxsw_sp->router->rifs);
5985}
5986
Petr Machata38ebc0f2017-09-02 23:49:17 +02005987static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp)
5988{
5989 mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr;
Petr Machata1012b9a2017-09-02 23:49:23 +02005990 INIT_LIST_HEAD(&mlxsw_sp->router->ipip_list);
Petr Machata38ebc0f2017-09-02 23:49:17 +02005991 return 0;
5992}
5993
5994static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp)
5995{
Petr Machata1012b9a2017-09-02 23:49:23 +02005996 WARN_ON(!list_empty(&mlxsw_sp->router->ipip_list));
Petr Machata38ebc0f2017-09-02 23:49:17 +02005997}
5998
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005999static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
6000{
Ido Schimmel7e39d112017-05-16 19:38:28 +02006001 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006002
6003 /* Flush pending FIB notifications and then flush the device's
6004 * table before requesting another dump. The FIB notification
6005 * block is unregistered, so no need to take RTNL.
6006 */
6007 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02006008 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
6009 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006010}
6011
Ido Schimmel4724ba562017-03-10 08:53:39 +01006012static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
6013{
6014 char rgcr_pl[MLXSW_REG_RGCR_LEN];
6015 u64 max_rifs;
6016 int err;
6017
6018 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
6019 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01006020 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01006021
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02006022 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01006023 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
6024 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
6025 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006026 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01006027 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01006028}
6029
6030static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
6031{
6032 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01006033
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02006034 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01006035 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01006036}
6037
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006038int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
6039{
Ido Schimmel9011b672017-05-16 19:38:25 +02006040 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006041 int err;
6042
Ido Schimmel9011b672017-05-16 19:38:25 +02006043 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
6044 if (!router)
6045 return -ENOMEM;
6046 mlxsw_sp->router = router;
6047 router->mlxsw_sp = mlxsw_sp;
6048
6049 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006050 err = __mlxsw_sp_router_init(mlxsw_sp);
6051 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02006052 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006053
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006054 err = mlxsw_sp_rifs_init(mlxsw_sp);
6055 if (err)
6056 goto err_rifs_init;
6057
Petr Machata38ebc0f2017-09-02 23:49:17 +02006058 err = mlxsw_sp_ipips_init(mlxsw_sp);
6059 if (err)
6060 goto err_ipips_init;
6061
Ido Schimmel9011b672017-05-16 19:38:25 +02006062 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01006063 &mlxsw_sp_nexthop_ht_params);
6064 if (err)
6065 goto err_nexthop_ht_init;
6066
Ido Schimmel9011b672017-05-16 19:38:25 +02006067 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01006068 &mlxsw_sp_nexthop_group_ht_params);
6069 if (err)
6070 goto err_nexthop_group_ht_init;
6071
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02006072 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_list);
Ido Schimmel8494ab02017-03-24 08:02:47 +01006073 err = mlxsw_sp_lpm_init(mlxsw_sp);
6074 if (err)
6075 goto err_lpm_init;
6076
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006077 err = mlxsw_sp_vrs_init(mlxsw_sp);
6078 if (err)
6079 goto err_vrs_init;
6080
Ido Schimmel8c9583a2016-10-27 15:12:57 +02006081 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006082 if (err)
6083 goto err_neigh_init;
6084
Ido Schimmel7e39d112017-05-16 19:38:28 +02006085 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
6086 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006087 mlxsw_sp_router_fib_dump_flush);
6088 if (err)
6089 goto err_register_fib_notifier;
6090
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006091 return 0;
6092
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006093err_register_fib_notifier:
6094 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006095err_neigh_init:
6096 mlxsw_sp_vrs_fini(mlxsw_sp);
6097err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01006098 mlxsw_sp_lpm_fini(mlxsw_sp);
6099err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02006100 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01006101err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02006102 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01006103err_nexthop_ht_init:
Petr Machata38ebc0f2017-09-02 23:49:17 +02006104 mlxsw_sp_ipips_fini(mlxsw_sp);
6105err_ipips_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006106 mlxsw_sp_rifs_fini(mlxsw_sp);
6107err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006108 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006109err_router_init:
6110 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006111 return err;
6112}
6113
6114void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
6115{
Ido Schimmel7e39d112017-05-16 19:38:28 +02006116 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006117 mlxsw_sp_neigh_fini(mlxsw_sp);
6118 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01006119 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006120 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
6121 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Petr Machata38ebc0f2017-09-02 23:49:17 +02006122 mlxsw_sp_ipips_fini(mlxsw_sp);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006123 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006124 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006125 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006126}