blob: 65e59a989084d02286739017deb13434699a9331 [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 Sharshevskyba31d362017-08-14 21:09:19 +02002071static struct fib_info *
2072mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
2073{
2074 return nh_grp->priv;
2075}
2076
2077struct mlxsw_sp_nexthop_group_cmp_arg {
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002078 enum mlxsw_sp_l3proto proto;
2079 union {
2080 struct fib_info *fi;
2081 struct mlxsw_sp_fib6_entry *fib6_entry;
2082 };
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002083};
2084
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002085static bool
2086mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,
2087 const struct in6_addr *gw, int ifindex)
2088{
2089 int i;
2090
2091 for (i = 0; i < nh_grp->count; i++) {
2092 const struct mlxsw_sp_nexthop *nh;
2093
2094 nh = &nh_grp->nexthops[i];
2095 if (nh->ifindex == ifindex &&
2096 ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))
2097 return true;
2098 }
2099
2100 return false;
2101}
2102
2103static bool
2104mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
2105 const struct mlxsw_sp_fib6_entry *fib6_entry)
2106{
2107 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2108
2109 if (nh_grp->count != fib6_entry->nrt6)
2110 return false;
2111
2112 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2113 struct in6_addr *gw;
2114 int ifindex;
2115
2116 ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex;
2117 gw = &mlxsw_sp_rt6->rt->rt6i_gateway;
2118 if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex))
2119 return false;
2120 }
2121
2122 return true;
2123}
2124
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002125static int
2126mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
2127{
2128 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
2129 const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
2130
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002131 switch (cmp_arg->proto) {
2132 case MLXSW_SP_L3_PROTO_IPV4:
2133 return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
2134 case MLXSW_SP_L3_PROTO_IPV6:
2135 return !mlxsw_sp_nexthop6_group_cmp(nh_grp,
2136 cmp_arg->fib6_entry);
2137 default:
2138 WARN_ON(1);
2139 return 1;
2140 }
2141}
2142
2143static int
2144mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp)
2145{
2146 return nh_grp->neigh_tbl->family;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002147}
2148
2149static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
2150{
2151 const struct mlxsw_sp_nexthop_group *nh_grp = data;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002152 const struct mlxsw_sp_nexthop *nh;
2153 struct fib_info *fi;
2154 unsigned int val;
2155 int i;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002156
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002157 switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
2158 case AF_INET:
2159 fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
2160 return jhash(&fi, sizeof(fi), seed);
2161 case AF_INET6:
2162 val = nh_grp->count;
2163 for (i = 0; i < nh_grp->count; i++) {
2164 nh = &nh_grp->nexthops[i];
2165 val ^= nh->ifindex;
2166 }
2167 return jhash(&val, sizeof(val), seed);
2168 default:
2169 WARN_ON(1);
2170 return 0;
2171 }
2172}
2173
2174static u32
2175mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
2176{
2177 unsigned int val = fib6_entry->nrt6;
2178 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2179 struct net_device *dev;
2180
2181 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2182 dev = mlxsw_sp_rt6->rt->dst.dev;
2183 val ^= dev->ifindex;
2184 }
2185
2186 return jhash(&val, sizeof(val), seed);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002187}
2188
2189static u32
2190mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
2191{
2192 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
2193
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002194 switch (cmp_arg->proto) {
2195 case MLXSW_SP_L3_PROTO_IPV4:
2196 return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
2197 case MLXSW_SP_L3_PROTO_IPV6:
2198 return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);
2199 default:
2200 WARN_ON(1);
2201 return 0;
2202 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002203}
2204
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002205static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002206 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002207 .hashfn = mlxsw_sp_nexthop_group_hash,
2208 .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj,
2209 .obj_cmpfn = mlxsw_sp_nexthop_group_cmp,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002210};
2211
2212static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
2213 struct mlxsw_sp_nexthop_group *nh_grp)
2214{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002215 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
2216 !nh_grp->gateway)
2217 return 0;
2218
Ido Schimmel9011b672017-05-16 19:38:25 +02002219 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002220 &nh_grp->ht_node,
2221 mlxsw_sp_nexthop_group_ht_params);
2222}
2223
2224static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
2225 struct mlxsw_sp_nexthop_group *nh_grp)
2226{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002227 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
2228 !nh_grp->gateway)
2229 return;
2230
Ido Schimmel9011b672017-05-16 19:38:25 +02002231 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002232 &nh_grp->ht_node,
2233 mlxsw_sp_nexthop_group_ht_params);
2234}
2235
2236static struct mlxsw_sp_nexthop_group *
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002237mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
2238 struct fib_info *fi)
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002239{
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002240 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
2241
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002242 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002243 cmp_arg.fi = fi;
2244 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
2245 &cmp_arg,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002246 mlxsw_sp_nexthop_group_ht_params);
2247}
2248
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002249static struct mlxsw_sp_nexthop_group *
2250mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,
2251 struct mlxsw_sp_fib6_entry *fib6_entry)
2252{
2253 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
2254
2255 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6;
2256 cmp_arg.fib6_entry = fib6_entry;
2257 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
2258 &cmp_arg,
2259 mlxsw_sp_nexthop_group_ht_params);
2260}
2261
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002262static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
2263 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
2264 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
2265 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
2266};
2267
2268static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
2269 struct mlxsw_sp_nexthop *nh)
2270{
Ido Schimmel9011b672017-05-16 19:38:25 +02002271 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002272 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
2273}
2274
2275static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
2276 struct mlxsw_sp_nexthop *nh)
2277{
Ido Schimmel9011b672017-05-16 19:38:25 +02002278 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002279 mlxsw_sp_nexthop_ht_params);
2280}
2281
Ido Schimmelad178c82017-02-08 11:16:40 +01002282static struct mlxsw_sp_nexthop *
2283mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
2284 struct mlxsw_sp_nexthop_key key)
2285{
Ido Schimmel9011b672017-05-16 19:38:25 +02002286 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01002287 mlxsw_sp_nexthop_ht_params);
2288}
2289
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002290static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01002291 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002292 u32 adj_index, u16 ecmp_size,
2293 u32 new_adj_index,
2294 u16 new_ecmp_size)
2295{
2296 char raleu_pl[MLXSW_REG_RALEU_LEN];
2297
Ido Schimmel1a9234e662016-09-19 08:29:26 +02002298 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01002299 (enum mlxsw_reg_ralxx_protocol) fib->proto,
2300 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02002301 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002302 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
2303}
2304
2305static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
2306 struct mlxsw_sp_nexthop_group *nh_grp,
2307 u32 old_adj_index, u16 old_ecmp_size)
2308{
2309 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002310 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002311 int err;
2312
2313 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01002314 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002315 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002316 fib = fib_entry->fib_node->fib;
2317 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002318 old_adj_index,
2319 old_ecmp_size,
2320 nh_grp->adj_index,
2321 nh_grp->ecmp_size);
2322 if (err)
2323 return err;
2324 }
2325 return 0;
2326}
2327
2328static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
2329 struct mlxsw_sp_nexthop *nh)
2330{
2331 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
2332 char ratr_pl[MLXSW_REG_RATR_LEN];
2333
2334 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
Petr Machata89e41982017-09-02 23:49:15 +02002335 true, MLXSW_REG_RATR_TYPE_ETHERNET,
2336 adj_index, neigh_entry->rif);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002337 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
2338 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
2339}
2340
Petr Machata1012b9a2017-09-02 23:49:23 +02002341static int mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp,
2342 u32 adj_index,
2343 struct mlxsw_sp_nexthop *nh)
2344{
2345 const struct mlxsw_sp_ipip_ops *ipip_ops;
2346
2347 ipip_ops = mlxsw_sp->router->ipip_ops_arr[nh->ipip_entry->ipipt];
2348 return ipip_ops->nexthop_update(mlxsw_sp, adj_index, nh->ipip_entry);
2349}
2350
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002351static int
Petr Machata35225e42017-09-02 23:49:22 +02002352mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp,
2353 struct mlxsw_sp_nexthop_group *nh_grp,
2354 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002355{
2356 u32 adj_index = nh_grp->adj_index; /* base */
2357 struct mlxsw_sp_nexthop *nh;
2358 int i;
2359 int err;
2360
2361 for (i = 0; i < nh_grp->count; i++) {
2362 nh = &nh_grp->nexthops[i];
2363
2364 if (!nh->should_offload) {
2365 nh->offloaded = 0;
2366 continue;
2367 }
2368
Ido Schimmela59b7e02017-01-23 11:11:42 +01002369 if (nh->update || reallocate) {
Petr Machata35225e42017-09-02 23:49:22 +02002370 switch (nh->type) {
2371 case MLXSW_SP_NEXTHOP_TYPE_ETH:
2372 err = mlxsw_sp_nexthop_mac_update
2373 (mlxsw_sp, adj_index, nh);
2374 break;
Petr Machata1012b9a2017-09-02 23:49:23 +02002375 case MLXSW_SP_NEXTHOP_TYPE_IPIP:
2376 err = mlxsw_sp_nexthop_ipip_update
2377 (mlxsw_sp, adj_index, nh);
2378 break;
Petr Machata35225e42017-09-02 23:49:22 +02002379 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002380 if (err)
2381 return err;
2382 nh->update = 0;
2383 nh->offloaded = 1;
2384 }
2385 adj_index++;
2386 }
2387 return 0;
2388}
2389
Ido Schimmel1819ae32017-07-21 18:04:28 +02002390static bool
2391mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2392 const struct mlxsw_sp_fib_entry *fib_entry);
2393
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002394static int
2395mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
2396 struct mlxsw_sp_nexthop_group *nh_grp)
2397{
2398 struct mlxsw_sp_fib_entry *fib_entry;
2399 int err;
2400
2401 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02002402 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2403 fib_entry))
2404 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002405 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2406 if (err)
2407 return err;
2408 }
2409 return 0;
2410}
2411
2412static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02002413mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2414 enum mlxsw_reg_ralue_op op, int err);
2415
2416static void
2417mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
2418{
2419 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
2420 struct mlxsw_sp_fib_entry *fib_entry;
2421
2422 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
2423 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2424 fib_entry))
2425 continue;
2426 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2427 }
2428}
2429
2430static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002431mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
2432 struct mlxsw_sp_nexthop_group *nh_grp)
2433{
2434 struct mlxsw_sp_nexthop *nh;
2435 bool offload_change = false;
2436 u32 adj_index;
2437 u16 ecmp_size = 0;
2438 bool old_adj_index_valid;
2439 u32 old_adj_index;
2440 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002441 int i;
2442 int err;
2443
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002444 if (!nh_grp->gateway) {
2445 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2446 return;
2447 }
2448
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002449 for (i = 0; i < nh_grp->count; i++) {
2450 nh = &nh_grp->nexthops[i];
2451
Petr Machata56b8a9e2017-07-31 09:27:29 +02002452 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002453 offload_change = true;
2454 if (nh->should_offload)
2455 nh->update = 1;
2456 }
2457 if (nh->should_offload)
2458 ecmp_size++;
2459 }
2460 if (!offload_change) {
2461 /* Nothing was added or removed, so no need to reallocate. Just
2462 * update MAC on existing adjacency indexes.
2463 */
Petr Machata35225e42017-09-02 23:49:22 +02002464 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002465 if (err) {
2466 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2467 goto set_trap;
2468 }
2469 return;
2470 }
2471 if (!ecmp_size)
2472 /* No neigh of this group is connected so we just set
2473 * the trap and let everthing flow through kernel.
2474 */
2475 goto set_trap;
2476
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01002477 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
2478 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002479 /* We ran out of KVD linear space, just set the
2480 * trap and let everything flow through kernel.
2481 */
2482 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
2483 goto set_trap;
2484 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002485 old_adj_index_valid = nh_grp->adj_index_valid;
2486 old_adj_index = nh_grp->adj_index;
2487 old_ecmp_size = nh_grp->ecmp_size;
2488 nh_grp->adj_index_valid = 1;
2489 nh_grp->adj_index = adj_index;
2490 nh_grp->ecmp_size = ecmp_size;
Petr Machata35225e42017-09-02 23:49:22 +02002491 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002492 if (err) {
2493 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2494 goto set_trap;
2495 }
2496
2497 if (!old_adj_index_valid) {
2498 /* The trap was set for fib entries, so we have to call
2499 * fib entry update to unset it and use adjacency index.
2500 */
2501 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2502 if (err) {
2503 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
2504 goto set_trap;
2505 }
2506 return;
2507 }
2508
2509 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
2510 old_adj_index, old_ecmp_size);
2511 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
2512 if (err) {
2513 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
2514 goto set_trap;
2515 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02002516
2517 /* Offload state within the group changed, so update the flags. */
2518 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
2519
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002520 return;
2521
2522set_trap:
2523 old_adj_index_valid = nh_grp->adj_index_valid;
2524 nh_grp->adj_index_valid = 0;
2525 for (i = 0; i < nh_grp->count; i++) {
2526 nh = &nh_grp->nexthops[i];
2527 nh->offloaded = 0;
2528 }
2529 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2530 if (err)
2531 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
2532 if (old_adj_index_valid)
2533 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
2534}
2535
2536static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
2537 bool removing)
2538{
Petr Machata213666a2017-07-31 09:27:30 +02002539 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002540 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02002541 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002542 nh->should_offload = 0;
2543 nh->update = 1;
2544}
2545
2546static void
2547mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
2548 struct mlxsw_sp_neigh_entry *neigh_entry,
2549 bool removing)
2550{
2551 struct mlxsw_sp_nexthop *nh;
2552
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002553 list_for_each_entry(nh, &neigh_entry->nexthop_list,
2554 neigh_list_node) {
2555 __mlxsw_sp_nexthop_neigh_update(nh, removing);
2556 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2557 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002558}
2559
Ido Schimmel9665b742017-02-08 11:16:42 +01002560static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002561 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002562{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002563 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002564 return;
2565
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002566 nh->rif = rif;
2567 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01002568}
2569
2570static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
2571{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002572 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002573 return;
2574
2575 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002576 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01002577}
2578
Ido Schimmela8c97012017-02-08 11:16:35 +01002579static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
2580 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002581{
2582 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002583 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002584 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002585 int err;
2586
Ido Schimmelad178c82017-02-08 11:16:40 +01002587 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01002588 return 0;
2589
Jiri Pirko33b13412016-11-10 12:31:04 +01002590 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02002591 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01002592 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01002593 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01002594 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002595 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01002596 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002597 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
2598 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01002599 if (IS_ERR(n))
2600 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002601 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01002602 }
2603 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
2604 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002605 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
2606 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002607 err = -EINVAL;
2608 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002609 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002610 }
Yotam Gigib2157142016-07-05 11:27:51 +02002611
2612 /* If that is the first nexthop connected to that neigh, add to
2613 * nexthop_neighs_list
2614 */
2615 if (list_empty(&neigh_entry->nexthop_list))
2616 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02002617 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02002618
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002619 nh->neigh_entry = neigh_entry;
2620 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
2621 read_lock_bh(&n->lock);
2622 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002623 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002624 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01002625 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002626
2627 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002628
2629err_neigh_entry_create:
2630 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002631 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002632}
2633
Ido Schimmela8c97012017-02-08 11:16:35 +01002634static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
2635 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002636{
2637 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01002638 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002639
Ido Schimmelb8399a12017-02-08 11:16:33 +01002640 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01002641 return;
2642 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01002643
Ido Schimmel58312122016-12-23 09:32:50 +01002644 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002645 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01002646 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02002647
2648 /* If that is the last nexthop connected to that neigh, remove from
2649 * nexthop_neighs_list
2650 */
Ido Schimmele58be792017-02-08 11:16:28 +01002651 if (list_empty(&neigh_entry->nexthop_list))
2652 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02002653
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002654 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
2655 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
2656
2657 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01002658}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002659
Petr Machata6ddb7422017-09-02 23:49:19 +02002660static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp,
2661 const struct net_device *dev,
2662 enum mlxsw_sp_ipip_type *p_type)
2663{
2664 struct mlxsw_sp_router *router = mlxsw_sp->router;
2665 const struct mlxsw_sp_ipip_ops *ipip_ops;
2666 enum mlxsw_sp_ipip_type ipipt;
2667
2668 for (ipipt = 0; ipipt < MLXSW_SP_IPIP_TYPE_MAX; ++ipipt) {
2669 ipip_ops = router->ipip_ops_arr[ipipt];
2670 if (dev->type == ipip_ops->dev_type) {
2671 if (p_type)
2672 *p_type = ipipt;
2673 return true;
2674 }
2675 }
2676 return false;
2677}
2678
Petr Machata1012b9a2017-09-02 23:49:23 +02002679static int mlxsw_sp_nexthop_ipip_init(struct mlxsw_sp *mlxsw_sp,
2680 enum mlxsw_sp_ipip_type ipipt,
2681 struct mlxsw_sp_nexthop *nh,
2682 struct net_device *ol_dev)
2683{
2684 if (!nh->nh_grp->gateway || nh->ipip_entry)
2685 return 0;
2686
2687 nh->ipip_entry = mlxsw_sp_ipip_entry_get(mlxsw_sp, ipipt, ol_dev);
2688 if (IS_ERR(nh->ipip_entry))
2689 return PTR_ERR(nh->ipip_entry);
2690
2691 __mlxsw_sp_nexthop_neigh_update(nh, false);
2692 return 0;
2693}
2694
2695static void mlxsw_sp_nexthop_ipip_fini(struct mlxsw_sp *mlxsw_sp,
2696 struct mlxsw_sp_nexthop *nh)
2697{
2698 struct mlxsw_sp_ipip_entry *ipip_entry = nh->ipip_entry;
2699
2700 if (!ipip_entry)
2701 return;
2702
2703 __mlxsw_sp_nexthop_neigh_update(nh, true);
2704 mlxsw_sp_ipip_entry_put(mlxsw_sp, ipip_entry);
2705 nh->ipip_entry = NULL;
2706}
2707
2708static bool mlxsw_sp_nexthop4_ipip_type(const struct mlxsw_sp *mlxsw_sp,
2709 const struct fib_nh *fib_nh,
2710 enum mlxsw_sp_ipip_type *p_ipipt)
2711{
2712 struct net_device *dev = fib_nh->nh_dev;
2713
2714 return dev &&
2715 fib_nh->nh_parent->fib_type == RTN_UNICAST &&
2716 mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, p_ipipt);
2717}
2718
Petr Machata35225e42017-09-02 23:49:22 +02002719static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp,
2720 struct mlxsw_sp_nexthop *nh)
2721{
2722 switch (nh->type) {
2723 case MLXSW_SP_NEXTHOP_TYPE_ETH:
2724 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2725 mlxsw_sp_nexthop_rif_fini(nh);
2726 break;
Petr Machata1012b9a2017-09-02 23:49:23 +02002727 case MLXSW_SP_NEXTHOP_TYPE_IPIP:
2728 mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh);
2729 break;
Petr Machata35225e42017-09-02 23:49:22 +02002730 }
2731}
2732
2733static int mlxsw_sp_nexthop4_type_init(struct mlxsw_sp *mlxsw_sp,
2734 struct mlxsw_sp_nexthop *nh,
2735 struct fib_nh *fib_nh)
2736{
Petr Machata1012b9a2017-09-02 23:49:23 +02002737 struct mlxsw_sp_router *router = mlxsw_sp->router;
Petr Machata35225e42017-09-02 23:49:22 +02002738 struct net_device *dev = fib_nh->nh_dev;
Petr Machata1012b9a2017-09-02 23:49:23 +02002739 enum mlxsw_sp_ipip_type ipipt;
Petr Machata35225e42017-09-02 23:49:22 +02002740 struct mlxsw_sp_rif *rif;
2741 int err;
2742
Petr Machata1012b9a2017-09-02 23:49:23 +02002743 if (mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fib_nh, &ipipt) &&
2744 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev,
2745 MLXSW_SP_L3_PROTO_IPV4)) {
2746 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
2747 return mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev);
2748 }
2749
Petr Machata35225e42017-09-02 23:49:22 +02002750 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
2751 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
2752 if (!rif)
2753 return 0;
2754
2755 mlxsw_sp_nexthop_rif_init(nh, rif);
2756 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2757 if (err)
2758 goto err_neigh_init;
2759
2760 return 0;
2761
2762err_neigh_init:
2763 mlxsw_sp_nexthop_rif_fini(nh);
2764 return err;
2765}
2766
2767static void mlxsw_sp_nexthop4_type_fini(struct mlxsw_sp *mlxsw_sp,
2768 struct mlxsw_sp_nexthop *nh)
2769{
2770 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
2771}
2772
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002773static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
2774 struct mlxsw_sp_nexthop_group *nh_grp,
2775 struct mlxsw_sp_nexthop *nh,
2776 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002777{
2778 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002779 struct in_device *in_dev;
Ido Schimmela8c97012017-02-08 11:16:35 +01002780 int err;
2781
2782 nh->nh_grp = nh_grp;
2783 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002784 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01002785 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
2786 if (err)
2787 return err;
2788
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02002789 list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list);
2790
Ido Schimmel97989ee2017-03-10 08:53:38 +01002791 if (!dev)
2792 return 0;
2793
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002794 in_dev = __in_dev_get_rtnl(dev);
2795 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
2796 fib_nh->nh_flags & RTNH_F_LINKDOWN)
2797 return 0;
2798
Petr Machata35225e42017-09-02 23:49:22 +02002799 err = mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01002800 if (err)
2801 goto err_nexthop_neigh_init;
2802
2803 return 0;
2804
2805err_nexthop_neigh_init:
2806 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
2807 return err;
2808}
2809
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002810static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
2811 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002812{
Petr Machata35225e42017-09-02 23:49:22 +02002813 mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02002814 list_del(&nh->router_list_node);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002815 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002816}
2817
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002818static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
2819 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01002820{
2821 struct mlxsw_sp_nexthop_key key;
2822 struct mlxsw_sp_nexthop *nh;
Ido Schimmelad178c82017-02-08 11:16:40 +01002823
Ido Schimmel9011b672017-05-16 19:38:25 +02002824 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01002825 return;
2826
2827 key.fib_nh = fib_nh;
2828 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
2829 if (WARN_ON_ONCE(!nh))
2830 return;
2831
Ido Schimmelad178c82017-02-08 11:16:40 +01002832 switch (event) {
2833 case FIB_EVENT_NH_ADD:
Petr Machata35225e42017-09-02 23:49:22 +02002834 mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002835 break;
2836 case FIB_EVENT_NH_DEL:
Petr Machata35225e42017-09-02 23:49:22 +02002837 mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002838 break;
2839 }
2840
2841 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2842}
2843
Ido Schimmel9665b742017-02-08 11:16:42 +01002844static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002845 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002846{
2847 struct mlxsw_sp_nexthop *nh, *tmp;
2848
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002849 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Petr Machata35225e42017-09-02 23:49:22 +02002850 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002851 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2852 }
2853}
2854
Petr Machata9b014512017-09-02 23:49:20 +02002855static bool mlxsw_sp_fi_is_gateway(const struct mlxsw_sp *mlxsw_sp,
2856 const struct fib_info *fi)
2857{
Petr Machata1012b9a2017-09-02 23:49:23 +02002858 return fi->fib_nh->nh_scope == RT_SCOPE_LINK ||
2859 mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fi->fib_nh, NULL);
Petr Machata9b014512017-09-02 23:49:20 +02002860}
2861
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002862static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002863mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002864{
2865 struct mlxsw_sp_nexthop_group *nh_grp;
2866 struct mlxsw_sp_nexthop *nh;
2867 struct fib_nh *fib_nh;
2868 size_t alloc_size;
2869 int i;
2870 int err;
2871
2872 alloc_size = sizeof(*nh_grp) +
2873 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2874 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2875 if (!nh_grp)
2876 return ERR_PTR(-ENOMEM);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002877 nh_grp->priv = fi;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002878 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002879 nh_grp->neigh_tbl = &arp_tbl;
2880
Petr Machata9b014512017-09-02 23:49:20 +02002881 nh_grp->gateway = mlxsw_sp_fi_is_gateway(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002882 nh_grp->count = fi->fib_nhs;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002883 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002884 for (i = 0; i < nh_grp->count; i++) {
2885 nh = &nh_grp->nexthops[i];
2886 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002887 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002888 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002889 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002890 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002891 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2892 if (err)
2893 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002894 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2895 return nh_grp;
2896
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002897err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002898err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002899 for (i--; i >= 0; i--) {
2900 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002901 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002902 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002903 fib_info_put(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002904 kfree(nh_grp);
2905 return ERR_PTR(err);
2906}
2907
2908static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002909mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2910 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002911{
2912 struct mlxsw_sp_nexthop *nh;
2913 int i;
2914
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002915 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002916 for (i = 0; i < nh_grp->count; i++) {
2917 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002918 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002919 }
Ido Schimmel58312122016-12-23 09:32:50 +01002920 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2921 WARN_ON_ONCE(nh_grp->adj_index_valid);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002922 fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002923 kfree(nh_grp);
2924}
2925
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002926static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2927 struct mlxsw_sp_fib_entry *fib_entry,
2928 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002929{
2930 struct mlxsw_sp_nexthop_group *nh_grp;
2931
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002932 nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002933 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002934 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002935 if (IS_ERR(nh_grp))
2936 return PTR_ERR(nh_grp);
2937 }
2938 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2939 fib_entry->nh_group = nh_grp;
2940 return 0;
2941}
2942
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002943static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2944 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002945{
2946 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2947
2948 list_del(&fib_entry->nexthop_group_node);
2949 if (!list_empty(&nh_grp->fib_list))
2950 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002951 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002952}
2953
Ido Schimmel013b20f2017-02-08 11:16:36 +01002954static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002955mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2956{
2957 struct mlxsw_sp_fib4_entry *fib4_entry;
2958
2959 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2960 common);
2961 return !fib4_entry->tos;
2962}
2963
2964static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01002965mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2966{
2967 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2968
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002969 switch (fib_entry->fib_node->fib->proto) {
2970 case MLXSW_SP_L3_PROTO_IPV4:
2971 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2972 return false;
2973 break;
2974 case MLXSW_SP_L3_PROTO_IPV6:
2975 break;
2976 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002977
Ido Schimmel013b20f2017-02-08 11:16:36 +01002978 switch (fib_entry->type) {
2979 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2980 return !!nh_group->adj_index_valid;
2981 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01002982 return !!nh_group->nh_rif;
Petr Machata4607f6d2017-09-02 23:49:25 +02002983 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
2984 return true;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002985 default:
2986 return false;
2987 }
2988}
2989
Ido Schimmel428b8512017-08-03 13:28:28 +02002990static struct mlxsw_sp_nexthop *
2991mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
2992 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
2993{
2994 int i;
2995
2996 for (i = 0; i < nh_grp->count; i++) {
2997 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2998 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2999
3000 if (nh->rif && nh->rif->dev == rt->dst.dev &&
3001 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
3002 &rt->rt6i_gateway))
3003 return nh;
3004 continue;
3005 }
3006
3007 return NULL;
3008}
3009
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003010static void
3011mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3012{
3013 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3014 int i;
3015
Petr Machata4607f6d2017-09-02 23:49:25 +02003016 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL ||
3017 fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP) {
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003018 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
3019 return;
3020 }
3021
3022 for (i = 0; i < nh_grp->count; i++) {
3023 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3024
3025 if (nh->offloaded)
3026 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
3027 else
3028 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
3029 }
3030}
3031
3032static void
3033mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3034{
3035 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3036 int i;
3037
3038 for (i = 0; i < nh_grp->count; i++) {
3039 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3040
3041 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
3042 }
3043}
3044
Ido Schimmel428b8512017-08-03 13:28:28 +02003045static void
3046mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3047{
3048 struct mlxsw_sp_fib6_entry *fib6_entry;
3049 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3050
3051 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
3052 common);
3053
3054 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
3055 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
Ido Schimmelfe400792017-08-15 09:09:49 +02003056 list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003057 return;
3058 }
3059
3060 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3061 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3062 struct mlxsw_sp_nexthop *nh;
3063
3064 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
3065 if (nh && nh->offloaded)
Ido Schimmelfe400792017-08-15 09:09:49 +02003066 mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003067 else
Ido Schimmelfe400792017-08-15 09:09:49 +02003068 mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003069 }
3070}
3071
3072static void
3073mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3074{
3075 struct mlxsw_sp_fib6_entry *fib6_entry;
3076 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3077
3078 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
3079 common);
3080 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3081 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3082
Ido Schimmelfe400792017-08-15 09:09:49 +02003083 rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003084 }
3085}
3086
Ido Schimmel013b20f2017-02-08 11:16:36 +01003087static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3088{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003089 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01003090 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003091 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003092 break;
3093 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003094 mlxsw_sp_fib6_entry_offload_set(fib_entry);
3095 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01003096 }
3097}
3098
3099static void
3100mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3101{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003102 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01003103 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003104 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003105 break;
3106 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003107 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
3108 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01003109 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01003110}
3111
3112static void
3113mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
3114 enum mlxsw_reg_ralue_op op, int err)
3115{
3116 switch (op) {
3117 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01003118 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
3119 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
3120 if (err)
3121 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02003122 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01003123 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02003124 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01003125 mlxsw_sp_fib_entry_offload_unset(fib_entry);
3126 return;
3127 default:
3128 return;
3129 }
3130}
3131
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003132static void
3133mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
3134 const struct mlxsw_sp_fib_entry *fib_entry,
3135 enum mlxsw_reg_ralue_op op)
3136{
3137 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
3138 enum mlxsw_reg_ralxx_protocol proto;
3139 u32 *p_dip;
3140
3141 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
3142
3143 switch (fib->proto) {
3144 case MLXSW_SP_L3_PROTO_IPV4:
3145 p_dip = (u32 *) fib_entry->fib_node->key.addr;
3146 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
3147 fib_entry->fib_node->key.prefix_len,
3148 *p_dip);
3149 break;
3150 case MLXSW_SP_L3_PROTO_IPV6:
3151 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
3152 fib_entry->fib_node->key.prefix_len,
3153 fib_entry->fib_node->key.addr);
3154 break;
3155 }
3156}
3157
3158static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
3159 struct mlxsw_sp_fib_entry *fib_entry,
3160 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003161{
3162 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003163 enum mlxsw_reg_ralue_trap_action trap_action;
3164 u16 trap_id = 0;
3165 u32 adjacency_index = 0;
3166 u16 ecmp_size = 0;
3167
3168 /* In case the nexthop group adjacency index is valid, use it
3169 * with provided ECMP size. Otherwise, setup trap and pass
3170 * traffic to kernel.
3171 */
Ido Schimmel4b411472017-02-08 11:16:37 +01003172 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003173 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
3174 adjacency_index = fib_entry->nh_group->adj_index;
3175 ecmp_size = fib_entry->nh_group->ecmp_size;
3176 } else {
3177 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
3178 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
3179 }
3180
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003181 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003182 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
3183 adjacency_index, ecmp_size);
3184 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3185}
3186
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003187static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
3188 struct mlxsw_sp_fib_entry *fib_entry,
3189 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003190{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003191 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003192 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003193 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01003194 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003195 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003196
3197 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
3198 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003199 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003200 } else {
3201 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
3202 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
3203 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003204
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003205 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003206 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
3207 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003208 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3209}
3210
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003211static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
3212 struct mlxsw_sp_fib_entry *fib_entry,
3213 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003214{
3215 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02003216
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003217 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003218 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
3219 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3220}
3221
Petr Machata4607f6d2017-09-02 23:49:25 +02003222static int
3223mlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp,
3224 struct mlxsw_sp_fib_entry *fib_entry,
3225 enum mlxsw_reg_ralue_op op)
3226{
3227 struct mlxsw_sp_ipip_entry *ipip_entry = fib_entry->decap.ipip_entry;
3228 const struct mlxsw_sp_ipip_ops *ipip_ops;
3229
3230 if (WARN_ON(!ipip_entry))
3231 return -EINVAL;
3232
3233 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
3234 return ipip_ops->fib_entry_op(mlxsw_sp, ipip_entry, op,
3235 fib_entry->decap.tunnel_index);
3236}
3237
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003238static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
3239 struct mlxsw_sp_fib_entry *fib_entry,
3240 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003241{
3242 switch (fib_entry->type) {
3243 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003244 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003245 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003246 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003247 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003248 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Petr Machata4607f6d2017-09-02 23:49:25 +02003249 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
3250 return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp,
3251 fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003252 }
3253 return -EINVAL;
3254}
3255
3256static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
3257 struct mlxsw_sp_fib_entry *fib_entry,
3258 enum mlxsw_reg_ralue_op op)
3259{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003260 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003261
Ido Schimmel013b20f2017-02-08 11:16:36 +01003262 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003263
Ido Schimmel013b20f2017-02-08 11:16:36 +01003264 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003265}
3266
3267static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
3268 struct mlxsw_sp_fib_entry *fib_entry)
3269{
Jiri Pirko7146da32016-09-01 10:37:41 +02003270 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
3271 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003272}
3273
3274static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
3275 struct mlxsw_sp_fib_entry *fib_entry)
3276{
3277 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
3278 MLXSW_REG_RALUE_OP_WRITE_DELETE);
3279}
3280
Jiri Pirko61c503f2016-07-04 08:23:11 +02003281static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01003282mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
3283 const struct fib_entry_notifier_info *fen_info,
3284 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003285{
Petr Machata4607f6d2017-09-02 23:49:25 +02003286 union mlxsw_sp_l3addr dip = { .addr4 = htonl(fen_info->dst) };
3287 struct net_device *dev = fen_info->fi->fib_dev;
3288 struct mlxsw_sp_ipip_entry *ipip_entry;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003289 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003290
Ido Schimmel97989ee2017-03-10 08:53:38 +01003291 switch (fen_info->type) {
Ido Schimmel97989ee2017-03-10 08:53:38 +01003292 case RTN_LOCAL:
Petr Machata4607f6d2017-09-02 23:49:25 +02003293 ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, dev,
3294 MLXSW_SP_L3_PROTO_IPV4, dip);
3295 if (ipip_entry) {
3296 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
3297 return mlxsw_sp_fib_entry_decap_init(mlxsw_sp,
3298 fib_entry,
3299 ipip_entry);
3300 }
3301 /* fall through */
3302 case RTN_BROADCAST:
Jiri Pirko61c503f2016-07-04 08:23:11 +02003303 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3304 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003305 case RTN_UNREACHABLE: /* fall through */
3306 case RTN_BLACKHOLE: /* fall through */
3307 case RTN_PROHIBIT:
3308 /* Packets hitting these routes need to be trapped, but
3309 * can do so with a lower priority than packets directed
3310 * at the host, so use action type local instead of trap.
3311 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003312 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003313 return 0;
3314 case RTN_UNICAST:
Petr Machata9b014512017-09-02 23:49:20 +02003315 if (mlxsw_sp_fi_is_gateway(mlxsw_sp, fi))
Ido Schimmel97989ee2017-03-10 08:53:38 +01003316 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
Petr Machata9b014512017-09-02 23:49:20 +02003317 else
3318 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003319 return 0;
3320 default:
3321 return -EINVAL;
3322 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003323}
3324
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003325static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003326mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
3327 struct mlxsw_sp_fib_node *fib_node,
3328 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02003329{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003330 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02003331 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003332 int err;
3333
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003334 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
3335 if (!fib4_entry)
3336 return ERR_PTR(-ENOMEM);
3337 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003338
3339 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
3340 if (err)
3341 goto err_fib4_entry_type_set;
3342
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003343 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003344 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003345 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003346
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003347 fib4_entry->prio = fen_info->fi->fib_priority;
3348 fib4_entry->tb_id = fen_info->tb_id;
3349 fib4_entry->type = fen_info->type;
3350 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003351
3352 fib_entry->fib_node = fib_node;
3353
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003354 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003355
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003356err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01003357err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003358 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003359 return ERR_PTR(err);
3360}
3361
3362static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003363 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003364{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003365 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003366 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003367}
3368
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003369static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003370mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3371 const struct fib_entry_notifier_info *fen_info)
3372{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003373 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003374 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02003375 struct mlxsw_sp_fib *fib;
3376 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003377
Ido Schimmel160e22a2017-07-18 10:10:20 +02003378 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
3379 if (!vr)
3380 return NULL;
3381 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
3382
3383 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
3384 sizeof(fen_info->dst),
3385 fen_info->dst_len);
3386 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003387 return NULL;
3388
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003389 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3390 if (fib4_entry->tb_id == fen_info->tb_id &&
3391 fib4_entry->tos == fen_info->tos &&
3392 fib4_entry->type == fen_info->type &&
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02003393 mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
3394 fen_info->fi) {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003395 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003396 }
3397 }
3398
3399 return NULL;
3400}
3401
3402static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
3403 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
3404 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
3405 .key_len = sizeof(struct mlxsw_sp_fib_key),
3406 .automatic_shrinking = true,
3407};
3408
3409static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
3410 struct mlxsw_sp_fib_node *fib_node)
3411{
3412 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
3413 mlxsw_sp_fib_ht_params);
3414}
3415
3416static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
3417 struct mlxsw_sp_fib_node *fib_node)
3418{
3419 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
3420 mlxsw_sp_fib_ht_params);
3421}
3422
3423static struct mlxsw_sp_fib_node *
3424mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
3425 size_t addr_len, unsigned char prefix_len)
3426{
3427 struct mlxsw_sp_fib_key key;
3428
3429 memset(&key, 0, sizeof(key));
3430 memcpy(key.addr, addr, addr_len);
3431 key.prefix_len = prefix_len;
3432 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
3433}
3434
3435static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01003436mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01003437 size_t addr_len, unsigned char prefix_len)
3438{
3439 struct mlxsw_sp_fib_node *fib_node;
3440
3441 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
3442 if (!fib_node)
3443 return NULL;
3444
3445 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003446 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003447 memcpy(fib_node->key.addr, addr, addr_len);
3448 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003449
3450 return fib_node;
3451}
3452
3453static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
3454{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003455 list_del(&fib_node->list);
3456 WARN_ON(!list_empty(&fib_node->entry_list));
3457 kfree(fib_node);
3458}
3459
3460static bool
3461mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
3462 const struct mlxsw_sp_fib_entry *fib_entry)
3463{
3464 return list_first_entry(&fib_node->entry_list,
3465 struct mlxsw_sp_fib_entry, list) == fib_entry;
3466}
3467
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003468static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
3469 struct mlxsw_sp_fib *fib,
3470 struct mlxsw_sp_fib_node *fib_node)
3471{
3472 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
3473 struct mlxsw_sp_lpm_tree *lpm_tree;
3474 int err;
3475
3476 /* Since the tree is shared between all virtual routers we must
3477 * make sure it contains all the required prefix lengths. This
3478 * can be computed by either adding the new prefix length to the
3479 * existing prefix usage of a bound tree, or by aggregating the
3480 * prefix lengths across all virtual routers and adding the new
3481 * one as well.
3482 */
3483 if (fib->lpm_tree)
3484 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
3485 &fib->lpm_tree->prefix_usage);
3486 else
3487 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
3488 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
3489
3490 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
3491 fib->proto);
3492 if (IS_ERR(lpm_tree))
3493 return PTR_ERR(lpm_tree);
3494
3495 if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id)
3496 return 0;
3497
3498 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
3499 if (err)
3500 return err;
3501
3502 return 0;
3503}
3504
3505static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
3506 struct mlxsw_sp_fib *fib)
3507{
3508 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
3509 struct mlxsw_sp_lpm_tree *lpm_tree;
3510
3511 /* Aggregate prefix lengths across all virtual routers to make
3512 * sure we only have used prefix lengths in the LPM tree.
3513 */
3514 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
3515 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
3516 fib->proto);
3517 if (IS_ERR(lpm_tree))
3518 goto err_tree_get;
3519 mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
3520
3521err_tree_get:
3522 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage))
3523 return;
3524 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
3525 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
3526 fib->lpm_tree = NULL;
3527}
3528
Ido Schimmel9aecce12017-02-09 10:28:42 +01003529static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
3530{
3531 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003532 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003533
3534 if (fib->prefix_ref_count[prefix_len]++ == 0)
3535 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
3536}
3537
3538static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
3539{
3540 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003541 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003542
3543 if (--fib->prefix_ref_count[prefix_len] == 0)
3544 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
3545}
3546
Ido Schimmel76610eb2017-03-10 08:53:41 +01003547static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
3548 struct mlxsw_sp_fib_node *fib_node,
3549 struct mlxsw_sp_fib *fib)
3550{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003551 int err;
3552
3553 err = mlxsw_sp_fib_node_insert(fib, fib_node);
3554 if (err)
3555 return err;
3556 fib_node->fib = fib;
3557
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003558 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node);
3559 if (err)
3560 goto err_fib_lpm_tree_link;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003561
3562 mlxsw_sp_fib_node_prefix_inc(fib_node);
3563
3564 return 0;
3565
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003566err_fib_lpm_tree_link:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003567 fib_node->fib = NULL;
3568 mlxsw_sp_fib_node_remove(fib, fib_node);
3569 return err;
3570}
3571
3572static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
3573 struct mlxsw_sp_fib_node *fib_node)
3574{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003575 struct mlxsw_sp_fib *fib = fib_node->fib;
3576
3577 mlxsw_sp_fib_node_prefix_dec(fib_node);
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003578 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003579 fib_node->fib = NULL;
3580 mlxsw_sp_fib_node_remove(fib, fib_node);
3581}
3582
Ido Schimmel9aecce12017-02-09 10:28:42 +01003583static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003584mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
3585 size_t addr_len, unsigned char prefix_len,
3586 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003587{
3588 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003589 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02003590 struct mlxsw_sp_vr *vr;
3591 int err;
3592
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003593 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02003594 if (IS_ERR(vr))
3595 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003596 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02003597
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003598 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003599 if (fib_node)
3600 return fib_node;
3601
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003602 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003603 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02003604 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003605 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02003606 }
Jiri Pirko5b004412016-09-01 10:37:40 +02003607
Ido Schimmel76610eb2017-03-10 08:53:41 +01003608 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
3609 if (err)
3610 goto err_fib_node_init;
3611
Ido Schimmel9aecce12017-02-09 10:28:42 +01003612 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003613
Ido Schimmel76610eb2017-03-10 08:53:41 +01003614err_fib_node_init:
3615 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003616err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003617 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003618 return ERR_PTR(err);
3619}
3620
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003621static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
3622 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02003623{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003624 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02003625
Ido Schimmel9aecce12017-02-09 10:28:42 +01003626 if (!list_empty(&fib_node->entry_list))
3627 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003628 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003629 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003630 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003631}
3632
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003633static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003634mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003635 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003636{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003637 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003638
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003639 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3640 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003641 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003642 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003643 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003644 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003645 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003646 if (fib4_entry->prio >= new4_entry->prio ||
3647 fib4_entry->tos < new4_entry->tos)
3648 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003649 }
3650
3651 return NULL;
3652}
3653
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003654static int
3655mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
3656 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003657{
3658 struct mlxsw_sp_fib_node *fib_node;
3659
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003660 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01003661 return -EINVAL;
3662
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003663 fib_node = fib4_entry->common.fib_node;
3664 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
3665 common.list) {
3666 if (fib4_entry->tb_id != new4_entry->tb_id ||
3667 fib4_entry->tos != new4_entry->tos ||
3668 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003669 break;
3670 }
3671
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003672 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01003673 return 0;
3674}
3675
Ido Schimmel9aecce12017-02-09 10:28:42 +01003676static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02003677mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003678 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003679{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003680 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003681 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003682
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003683 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003684
Ido Schimmel4283bce2017-02-09 10:28:43 +01003685 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003686 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
3687 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003688 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003689
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003690 /* Insert new entry before replaced one, so that we can later
3691 * remove the second.
3692 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003693 if (fib4_entry) {
3694 list_add_tail(&new4_entry->common.list,
3695 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003696 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003697 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003698
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003699 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3700 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003701 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003702 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003703 }
3704
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003705 if (fib4_entry)
3706 list_add(&new4_entry->common.list,
3707 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003708 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003709 list_add(&new4_entry->common.list,
3710 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003711 }
3712
3713 return 0;
3714}
3715
3716static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003717mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003718{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003719 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003720}
3721
Ido Schimmel80c238f2017-07-18 10:10:29 +02003722static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
3723 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003724{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003725 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3726
Ido Schimmel9aecce12017-02-09 10:28:42 +01003727 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3728 return 0;
3729
3730 /* To prevent packet loss, overwrite the previously offloaded
3731 * entry.
3732 */
3733 if (!list_is_singular(&fib_node->entry_list)) {
3734 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3735 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3736
3737 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
3738 }
3739
3740 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
3741}
3742
Ido Schimmel80c238f2017-07-18 10:10:29 +02003743static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
3744 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003745{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003746 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3747
Ido Schimmel9aecce12017-02-09 10:28:42 +01003748 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3749 return;
3750
3751 /* Promote the next entry by overwriting the deleted entry */
3752 if (!list_is_singular(&fib_node->entry_list)) {
3753 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3754 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3755
3756 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
3757 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
3758 return;
3759 }
3760
3761 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
3762}
3763
3764static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003765 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003766 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003767{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003768 int err;
3769
Ido Schimmel9efbee62017-07-18 10:10:28 +02003770 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003771 if (err)
3772 return err;
3773
Ido Schimmel80c238f2017-07-18 10:10:29 +02003774 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003775 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02003776 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003777
Ido Schimmel9aecce12017-02-09 10:28:42 +01003778 return 0;
3779
Ido Schimmel80c238f2017-07-18 10:10:29 +02003780err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003781 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003782 return err;
3783}
3784
3785static void
3786mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003787 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003788{
Ido Schimmel80c238f2017-07-18 10:10:29 +02003789 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003790 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Petr Machata4607f6d2017-09-02 23:49:25 +02003791
3792 if (fib4_entry->common.type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP)
3793 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003794}
3795
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003796static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003797 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003798 bool replace)
3799{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003800 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
3801 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003802
3803 if (!replace)
3804 return;
3805
3806 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003807 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003808
3809 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
3810 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003811 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003812}
3813
Ido Schimmel9aecce12017-02-09 10:28:42 +01003814static int
3815mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01003816 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003817 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003818{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003819 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003820 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003821 int err;
3822
Ido Schimmel9011b672017-05-16 19:38:25 +02003823 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003824 return 0;
3825
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003826 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
3827 &fen_info->dst, sizeof(fen_info->dst),
3828 fen_info->dst_len,
3829 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003830 if (IS_ERR(fib_node)) {
3831 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
3832 return PTR_ERR(fib_node);
3833 }
3834
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003835 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
3836 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003837 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003838 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003839 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003840 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003841
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003842 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003843 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003844 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003845 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
3846 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003847 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01003848
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003849 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003850
Jiri Pirko61c503f2016-07-04 08:23:11 +02003851 return 0;
3852
Ido Schimmel9aecce12017-02-09 10:28:42 +01003853err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003854 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003855err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003856 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003857 return err;
3858}
3859
Jiri Pirko37956d72016-10-20 16:05:43 +02003860static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
3861 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003862{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003863 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003864 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003865
Ido Schimmel9011b672017-05-16 19:38:25 +02003866 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02003867 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003868
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003869 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
3870 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02003871 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003872 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003873
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003874 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3875 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003876 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003877}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003878
Ido Schimmel428b8512017-08-03 13:28:28 +02003879static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
3880{
3881 /* Packets with link-local destination IP arriving to the router
3882 * are trapped to the CPU, so no need to program specific routes
3883 * for them.
3884 */
3885 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
3886 return true;
3887
3888 /* Multicast routes aren't supported, so ignore them. Neighbour
3889 * Discovery packets are specifically trapped.
3890 */
3891 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
3892 return true;
3893
3894 /* Cloned routes are irrelevant in the forwarding path. */
3895 if (rt->rt6i_flags & RTF_CACHE)
3896 return true;
3897
3898 return false;
3899}
3900
3901static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
3902{
3903 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3904
3905 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
3906 if (!mlxsw_sp_rt6)
3907 return ERR_PTR(-ENOMEM);
3908
3909 /* In case of route replace, replaced route is deleted with
3910 * no notification. Take reference to prevent accessing freed
3911 * memory.
3912 */
3913 mlxsw_sp_rt6->rt = rt;
3914 rt6_hold(rt);
3915
3916 return mlxsw_sp_rt6;
3917}
3918
3919#if IS_ENABLED(CONFIG_IPV6)
3920static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3921{
3922 rt6_release(rt);
3923}
3924#else
3925static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3926{
3927}
3928#endif
3929
3930static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3931{
3932 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
3933 kfree(mlxsw_sp_rt6);
3934}
3935
3936static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
3937{
3938 /* RTF_CACHE routes are ignored */
3939 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
3940}
3941
3942static struct rt6_info *
3943mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
3944{
3945 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
3946 list)->rt;
3947}
3948
3949static struct mlxsw_sp_fib6_entry *
3950mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003951 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003952{
3953 struct mlxsw_sp_fib6_entry *fib6_entry;
3954
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003955 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003956 return NULL;
3957
3958 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3959 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3960
3961 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
3962 * virtual router.
3963 */
3964 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3965 continue;
3966 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3967 break;
3968 if (rt->rt6i_metric < nrt->rt6i_metric)
3969 continue;
3970 if (rt->rt6i_metric == nrt->rt6i_metric &&
3971 mlxsw_sp_fib6_rt_can_mp(rt))
3972 return fib6_entry;
3973 if (rt->rt6i_metric > nrt->rt6i_metric)
3974 break;
3975 }
3976
3977 return NULL;
3978}
3979
3980static struct mlxsw_sp_rt6 *
3981mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
3982 const struct rt6_info *rt)
3983{
3984 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3985
3986 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3987 if (mlxsw_sp_rt6->rt == rt)
3988 return mlxsw_sp_rt6;
3989 }
3990
3991 return NULL;
3992}
3993
Petr Machata8f28a302017-09-02 23:49:24 +02003994static bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp,
3995 const struct rt6_info *rt,
3996 enum mlxsw_sp_ipip_type *ret)
3997{
3998 return rt->dst.dev &&
3999 mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->dst.dev, ret);
4000}
4001
Petr Machata35225e42017-09-02 23:49:22 +02004002static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp,
4003 struct mlxsw_sp_nexthop_group *nh_grp,
4004 struct mlxsw_sp_nexthop *nh,
4005 const struct rt6_info *rt)
Ido Schimmel428b8512017-08-03 13:28:28 +02004006{
Petr Machata8f28a302017-09-02 23:49:24 +02004007 struct mlxsw_sp_router *router = mlxsw_sp->router;
Ido Schimmel428b8512017-08-03 13:28:28 +02004008 struct net_device *dev = rt->dst.dev;
Petr Machata8f28a302017-09-02 23:49:24 +02004009 enum mlxsw_sp_ipip_type ipipt;
Ido Schimmel428b8512017-08-03 13:28:28 +02004010 struct mlxsw_sp_rif *rif;
4011 int err;
4012
Petr Machata8f28a302017-09-02 23:49:24 +02004013 if (mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, &ipipt) &&
4014 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev,
4015 MLXSW_SP_L3_PROTO_IPV6)) {
4016 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
4017 return mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev);
4018 }
4019
Petr Machata35225e42017-09-02 23:49:22 +02004020 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
Ido Schimmel428b8512017-08-03 13:28:28 +02004021 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4022 if (!rif)
4023 return 0;
4024 mlxsw_sp_nexthop_rif_init(nh, rif);
4025
4026 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
4027 if (err)
4028 goto err_nexthop_neigh_init;
4029
4030 return 0;
4031
4032err_nexthop_neigh_init:
4033 mlxsw_sp_nexthop_rif_fini(nh);
4034 return err;
4035}
4036
Petr Machata35225e42017-09-02 23:49:22 +02004037static void mlxsw_sp_nexthop6_type_fini(struct mlxsw_sp *mlxsw_sp,
4038 struct mlxsw_sp_nexthop *nh)
4039{
4040 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
4041}
4042
4043static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
4044 struct mlxsw_sp_nexthop_group *nh_grp,
4045 struct mlxsw_sp_nexthop *nh,
4046 const struct rt6_info *rt)
4047{
4048 struct net_device *dev = rt->dst.dev;
4049
4050 nh->nh_grp = nh_grp;
4051 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
4052
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02004053 list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list);
4054
Petr Machata35225e42017-09-02 23:49:22 +02004055 if (!dev)
4056 return 0;
4057 nh->ifindex = dev->ifindex;
4058
4059 return mlxsw_sp_nexthop6_type_init(mlxsw_sp, nh_grp, nh, rt);
4060}
4061
Ido Schimmel428b8512017-08-03 13:28:28 +02004062static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
4063 struct mlxsw_sp_nexthop *nh)
4064{
Petr Machata35225e42017-09-02 23:49:22 +02004065 mlxsw_sp_nexthop6_type_fini(mlxsw_sp, nh);
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02004066 list_del(&nh->router_list_node);
Ido Schimmel428b8512017-08-03 13:28:28 +02004067}
4068
Petr Machataf6050ee2017-09-02 23:49:21 +02004069static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp,
4070 const struct rt6_info *rt)
4071{
Petr Machata8f28a302017-09-02 23:49:24 +02004072 return rt->rt6i_flags & RTF_GATEWAY ||
4073 mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, NULL);
Petr Machataf6050ee2017-09-02 23:49:21 +02004074}
4075
Ido Schimmel428b8512017-08-03 13:28:28 +02004076static struct mlxsw_sp_nexthop_group *
4077mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
4078 struct mlxsw_sp_fib6_entry *fib6_entry)
4079{
4080 struct mlxsw_sp_nexthop_group *nh_grp;
4081 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4082 struct mlxsw_sp_nexthop *nh;
4083 size_t alloc_size;
4084 int i = 0;
4085 int err;
4086
4087 alloc_size = sizeof(*nh_grp) +
4088 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
4089 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
4090 if (!nh_grp)
4091 return ERR_PTR(-ENOMEM);
4092 INIT_LIST_HEAD(&nh_grp->fib_list);
4093#if IS_ENABLED(CONFIG_IPV6)
4094 nh_grp->neigh_tbl = &nd_tbl;
4095#endif
4096 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
4097 struct mlxsw_sp_rt6, list);
Petr Machataf6050ee2017-09-02 23:49:21 +02004098 nh_grp->gateway = mlxsw_sp_rt6_is_gateway(mlxsw_sp, mlxsw_sp_rt6->rt);
Ido Schimmel428b8512017-08-03 13:28:28 +02004099 nh_grp->count = fib6_entry->nrt6;
4100 for (i = 0; i < nh_grp->count; i++) {
4101 struct rt6_info *rt = mlxsw_sp_rt6->rt;
4102
4103 nh = &nh_grp->nexthops[i];
4104 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
4105 if (err)
4106 goto err_nexthop6_init;
4107 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
4108 }
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004109
4110 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
4111 if (err)
4112 goto err_nexthop_group_insert;
4113
Ido Schimmel428b8512017-08-03 13:28:28 +02004114 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
4115 return nh_grp;
4116
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004117err_nexthop_group_insert:
Ido Schimmel428b8512017-08-03 13:28:28 +02004118err_nexthop6_init:
4119 for (i--; i >= 0; i--) {
4120 nh = &nh_grp->nexthops[i];
4121 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
4122 }
4123 kfree(nh_grp);
4124 return ERR_PTR(err);
4125}
4126
4127static void
4128mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
4129 struct mlxsw_sp_nexthop_group *nh_grp)
4130{
4131 struct mlxsw_sp_nexthop *nh;
4132 int i = nh_grp->count;
4133
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004134 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Ido Schimmel428b8512017-08-03 13:28:28 +02004135 for (i--; i >= 0; i--) {
4136 nh = &nh_grp->nexthops[i];
4137 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
4138 }
4139 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
4140 WARN_ON(nh_grp->adj_index_valid);
4141 kfree(nh_grp);
4142}
4143
4144static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
4145 struct mlxsw_sp_fib6_entry *fib6_entry)
4146{
4147 struct mlxsw_sp_nexthop_group *nh_grp;
4148
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004149 nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry);
4150 if (!nh_grp) {
4151 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
4152 if (IS_ERR(nh_grp))
4153 return PTR_ERR(nh_grp);
4154 }
Ido Schimmel428b8512017-08-03 13:28:28 +02004155
4156 list_add_tail(&fib6_entry->common.nexthop_group_node,
4157 &nh_grp->fib_list);
4158 fib6_entry->common.nh_group = nh_grp;
4159
4160 return 0;
4161}
4162
4163static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
4164 struct mlxsw_sp_fib_entry *fib_entry)
4165{
4166 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
4167
4168 list_del(&fib_entry->nexthop_group_node);
4169 if (!list_empty(&nh_grp->fib_list))
4170 return;
4171 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
4172}
4173
4174static int
4175mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
4176 struct mlxsw_sp_fib6_entry *fib6_entry)
4177{
4178 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
4179 int err;
4180
4181 fib6_entry->common.nh_group = NULL;
4182 list_del(&fib6_entry->common.nexthop_group_node);
4183
4184 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
4185 if (err)
4186 goto err_nexthop6_group_get;
4187
4188 /* In case this entry is offloaded, then the adjacency index
4189 * currently associated with it in the device's table is that
4190 * of the old group. Start using the new one instead.
4191 */
4192 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
4193 if (err)
4194 goto err_fib_node_entry_add;
4195
4196 if (list_empty(&old_nh_grp->fib_list))
4197 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
4198
4199 return 0;
4200
4201err_fib_node_entry_add:
4202 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
4203err_nexthop6_group_get:
4204 list_add_tail(&fib6_entry->common.nexthop_group_node,
4205 &old_nh_grp->fib_list);
4206 fib6_entry->common.nh_group = old_nh_grp;
4207 return err;
4208}
4209
4210static int
4211mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
4212 struct mlxsw_sp_fib6_entry *fib6_entry,
4213 struct rt6_info *rt)
4214{
4215 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4216 int err;
4217
4218 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
4219 if (IS_ERR(mlxsw_sp_rt6))
4220 return PTR_ERR(mlxsw_sp_rt6);
4221
4222 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
4223 fib6_entry->nrt6++;
4224
4225 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
4226 if (err)
4227 goto err_nexthop6_group_update;
4228
4229 return 0;
4230
4231err_nexthop6_group_update:
4232 fib6_entry->nrt6--;
4233 list_del(&mlxsw_sp_rt6->list);
4234 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4235 return err;
4236}
4237
4238static void
4239mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
4240 struct mlxsw_sp_fib6_entry *fib6_entry,
4241 struct rt6_info *rt)
4242{
4243 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4244
4245 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
4246 if (WARN_ON(!mlxsw_sp_rt6))
4247 return;
4248
4249 fib6_entry->nrt6--;
4250 list_del(&mlxsw_sp_rt6->list);
4251 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
4252 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4253}
4254
Petr Machataf6050ee2017-09-02 23:49:21 +02004255static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp,
4256 struct mlxsw_sp_fib_entry *fib_entry,
Ido Schimmel428b8512017-08-03 13:28:28 +02004257 const struct rt6_info *rt)
4258{
4259 /* Packets hitting RTF_REJECT routes need to be discarded by the
4260 * stack. We can rely on their destination device not having a
4261 * RIF (it's the loopback device) and can thus use action type
4262 * local, which will cause them to be trapped with a lower
4263 * priority than packets that need to be locally received.
4264 */
Ido Schimmeld3b6d372017-09-01 10:58:55 +02004265 if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel428b8512017-08-03 13:28:28 +02004266 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
4267 else if (rt->rt6i_flags & RTF_REJECT)
4268 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Petr Machataf6050ee2017-09-02 23:49:21 +02004269 else if (mlxsw_sp_rt6_is_gateway(mlxsw_sp, rt))
Ido Schimmel428b8512017-08-03 13:28:28 +02004270 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
4271 else
4272 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
4273}
4274
4275static void
4276mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
4277{
4278 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
4279
4280 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
4281 list) {
4282 fib6_entry->nrt6--;
4283 list_del(&mlxsw_sp_rt6->list);
4284 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4285 }
4286}
4287
4288static struct mlxsw_sp_fib6_entry *
4289mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
4290 struct mlxsw_sp_fib_node *fib_node,
4291 struct rt6_info *rt)
4292{
4293 struct mlxsw_sp_fib6_entry *fib6_entry;
4294 struct mlxsw_sp_fib_entry *fib_entry;
4295 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4296 int err;
4297
4298 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
4299 if (!fib6_entry)
4300 return ERR_PTR(-ENOMEM);
4301 fib_entry = &fib6_entry->common;
4302
4303 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
4304 if (IS_ERR(mlxsw_sp_rt6)) {
4305 err = PTR_ERR(mlxsw_sp_rt6);
4306 goto err_rt6_create;
4307 }
4308
Petr Machataf6050ee2017-09-02 23:49:21 +02004309 mlxsw_sp_fib6_entry_type_set(mlxsw_sp, fib_entry, mlxsw_sp_rt6->rt);
Ido Schimmel428b8512017-08-03 13:28:28 +02004310
4311 INIT_LIST_HEAD(&fib6_entry->rt6_list);
4312 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
4313 fib6_entry->nrt6 = 1;
4314 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
4315 if (err)
4316 goto err_nexthop6_group_get;
4317
4318 fib_entry->fib_node = fib_node;
4319
4320 return fib6_entry;
4321
4322err_nexthop6_group_get:
4323 list_del(&mlxsw_sp_rt6->list);
4324 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4325err_rt6_create:
4326 kfree(fib6_entry);
4327 return ERR_PTR(err);
4328}
4329
4330static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
4331 struct mlxsw_sp_fib6_entry *fib6_entry)
4332{
4333 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
4334 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
4335 WARN_ON(fib6_entry->nrt6);
4336 kfree(fib6_entry);
4337}
4338
4339static struct mlxsw_sp_fib6_entry *
4340mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004341 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004342{
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004343 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
Ido Schimmel428b8512017-08-03 13:28:28 +02004344
4345 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4346 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4347
4348 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
4349 continue;
4350 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
4351 break;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004352 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
4353 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
4354 mlxsw_sp_fib6_rt_can_mp(nrt))
4355 return fib6_entry;
4356 if (mlxsw_sp_fib6_rt_can_mp(nrt))
4357 fallback = fallback ?: fib6_entry;
4358 }
Ido Schimmel428b8512017-08-03 13:28:28 +02004359 if (rt->rt6i_metric > nrt->rt6i_metric)
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004360 return fallback ?: fib6_entry;
Ido Schimmel428b8512017-08-03 13:28:28 +02004361 }
4362
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004363 return fallback;
Ido Schimmel428b8512017-08-03 13:28:28 +02004364}
4365
4366static int
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004367mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
4368 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004369{
4370 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
4371 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
4372 struct mlxsw_sp_fib6_entry *fib6_entry;
4373
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004374 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
4375
4376 if (replace && WARN_ON(!fib6_entry))
4377 return -EINVAL;
Ido Schimmel428b8512017-08-03 13:28:28 +02004378
4379 if (fib6_entry) {
4380 list_add_tail(&new6_entry->common.list,
4381 &fib6_entry->common.list);
4382 } else {
4383 struct mlxsw_sp_fib6_entry *last;
4384
4385 list_for_each_entry(last, &fib_node->entry_list, common.list) {
4386 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
4387
4388 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
4389 break;
4390 fib6_entry = last;
4391 }
4392
4393 if (fib6_entry)
4394 list_add(&new6_entry->common.list,
4395 &fib6_entry->common.list);
4396 else
4397 list_add(&new6_entry->common.list,
4398 &fib_node->entry_list);
4399 }
4400
4401 return 0;
4402}
4403
4404static void
4405mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
4406{
4407 list_del(&fib6_entry->common.list);
4408}
4409
4410static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004411 struct mlxsw_sp_fib6_entry *fib6_entry,
4412 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004413{
4414 int err;
4415
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004416 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004417 if (err)
4418 return err;
4419
4420 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
4421 if (err)
4422 goto err_fib_node_entry_add;
4423
4424 return 0;
4425
4426err_fib_node_entry_add:
4427 mlxsw_sp_fib6_node_list_remove(fib6_entry);
4428 return err;
4429}
4430
4431static void
4432mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
4433 struct mlxsw_sp_fib6_entry *fib6_entry)
4434{
4435 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
4436 mlxsw_sp_fib6_node_list_remove(fib6_entry);
4437}
4438
4439static struct mlxsw_sp_fib6_entry *
4440mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
4441 const struct rt6_info *rt)
4442{
4443 struct mlxsw_sp_fib6_entry *fib6_entry;
4444 struct mlxsw_sp_fib_node *fib_node;
4445 struct mlxsw_sp_fib *fib;
4446 struct mlxsw_sp_vr *vr;
4447
4448 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
4449 if (!vr)
4450 return NULL;
4451 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
4452
4453 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
4454 sizeof(rt->rt6i_dst.addr),
4455 rt->rt6i_dst.plen);
4456 if (!fib_node)
4457 return NULL;
4458
4459 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4460 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4461
4462 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
4463 rt->rt6i_metric == iter_rt->rt6i_metric &&
4464 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
4465 return fib6_entry;
4466 }
4467
4468 return NULL;
4469}
4470
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004471static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
4472 struct mlxsw_sp_fib6_entry *fib6_entry,
4473 bool replace)
4474{
4475 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
4476 struct mlxsw_sp_fib6_entry *replaced;
4477
4478 if (!replace)
4479 return;
4480
4481 replaced = list_next_entry(fib6_entry, common.list);
4482
4483 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
4484 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
4485 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4486}
4487
Ido Schimmel428b8512017-08-03 13:28:28 +02004488static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004489 struct rt6_info *rt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004490{
4491 struct mlxsw_sp_fib6_entry *fib6_entry;
4492 struct mlxsw_sp_fib_node *fib_node;
4493 int err;
4494
4495 if (mlxsw_sp->router->aborted)
4496 return 0;
4497
Ido Schimmelf36f5ac2017-08-03 13:28:30 +02004498 if (rt->rt6i_src.plen)
4499 return -EINVAL;
4500
Ido Schimmel428b8512017-08-03 13:28:28 +02004501 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4502 return 0;
4503
4504 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
4505 &rt->rt6i_dst.addr,
4506 sizeof(rt->rt6i_dst.addr),
4507 rt->rt6i_dst.plen,
4508 MLXSW_SP_L3_PROTO_IPV6);
4509 if (IS_ERR(fib_node))
4510 return PTR_ERR(fib_node);
4511
4512 /* Before creating a new entry, try to append route to an existing
4513 * multipath entry.
4514 */
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004515 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004516 if (fib6_entry) {
4517 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
4518 if (err)
4519 goto err_fib6_entry_nexthop_add;
4520 return 0;
4521 }
4522
4523 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
4524 if (IS_ERR(fib6_entry)) {
4525 err = PTR_ERR(fib6_entry);
4526 goto err_fib6_entry_create;
4527 }
4528
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004529 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004530 if (err)
4531 goto err_fib6_node_entry_link;
4532
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004533 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
4534
Ido Schimmel428b8512017-08-03 13:28:28 +02004535 return 0;
4536
4537err_fib6_node_entry_link:
4538 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4539err_fib6_entry_create:
4540err_fib6_entry_nexthop_add:
4541 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4542 return err;
4543}
4544
4545static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
4546 struct rt6_info *rt)
4547{
4548 struct mlxsw_sp_fib6_entry *fib6_entry;
4549 struct mlxsw_sp_fib_node *fib_node;
4550
4551 if (mlxsw_sp->router->aborted)
4552 return;
4553
4554 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4555 return;
4556
4557 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
4558 if (WARN_ON(!fib6_entry))
4559 return;
4560
4561 /* If route is part of a multipath entry, but not the last one
4562 * removed, then only reduce its nexthop group.
4563 */
4564 if (!list_is_singular(&fib6_entry->rt6_list)) {
4565 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
4566 return;
4567 }
4568
4569 fib_node = fib6_entry->common.fib_node;
4570
4571 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4572 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4573 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4574}
4575
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004576static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
4577 enum mlxsw_reg_ralxx_protocol proto,
4578 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004579{
4580 char ralta_pl[MLXSW_REG_RALTA_LEN];
4581 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004582 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004583
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004584 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004585 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
4586 if (err)
4587 return err;
4588
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004589 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004590 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
4591 if (err)
4592 return err;
4593
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004594 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004595 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004596 char raltb_pl[MLXSW_REG_RALTB_LEN];
4597 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004598
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004599 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004600 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
4601 raltb_pl);
4602 if (err)
4603 return err;
4604
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004605 mlxsw_reg_ralue_pack(ralue_pl, proto,
4606 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004607 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
4608 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
4609 ralue_pl);
4610 if (err)
4611 return err;
4612 }
4613
4614 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004615}
4616
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004617static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
4618{
4619 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
4620 int err;
4621
4622 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4623 MLXSW_SP_LPM_TREE_MIN);
4624 if (err)
4625 return err;
4626
4627 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
4628 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4629 MLXSW_SP_LPM_TREE_MIN + 1);
4630}
4631
Ido Schimmel9aecce12017-02-09 10:28:42 +01004632static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
4633 struct mlxsw_sp_fib_node *fib_node)
4634{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004635 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004636
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004637 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
4638 common.list) {
4639 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004640
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004641 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
4642 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02004643 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004644 /* Break when entry list is empty and node was freed.
4645 * Otherwise, we'll access freed memory in the next
4646 * iteration.
4647 */
4648 if (do_break)
4649 break;
4650 }
4651}
4652
Ido Schimmel428b8512017-08-03 13:28:28 +02004653static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
4654 struct mlxsw_sp_fib_node *fib_node)
4655{
4656 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
4657
4658 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
4659 common.list) {
4660 bool do_break = &tmp->common.list == &fib_node->entry_list;
4661
4662 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4663 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4664 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4665 if (do_break)
4666 break;
4667 }
4668}
4669
Ido Schimmel9aecce12017-02-09 10:28:42 +01004670static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
4671 struct mlxsw_sp_fib_node *fib_node)
4672{
Ido Schimmel76610eb2017-03-10 08:53:41 +01004673 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01004674 case MLXSW_SP_L3_PROTO_IPV4:
4675 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
4676 break;
4677 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02004678 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004679 break;
4680 }
4681}
4682
Ido Schimmel76610eb2017-03-10 08:53:41 +01004683static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
4684 struct mlxsw_sp_vr *vr,
4685 enum mlxsw_sp_l3proto proto)
4686{
4687 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
4688 struct mlxsw_sp_fib_node *fib_node, *tmp;
4689
4690 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
4691 bool do_break = &tmp->list == &fib->node_list;
4692
4693 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
4694 if (do_break)
4695 break;
4696 }
4697}
4698
Ido Schimmelac571de2016-11-14 11:26:32 +01004699static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004700{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004701 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004702
Jiri Pirkoc1a38312016-10-21 16:07:23 +02004703 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004704 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01004705
Ido Schimmel76610eb2017-03-10 08:53:41 +01004706 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004707 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01004708 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02004709
4710 /* If virtual router was only used for IPv4, then it's no
4711 * longer used.
4712 */
4713 if (!mlxsw_sp_vr_is_used(vr))
4714 continue;
4715 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004716 }
Ido Schimmelac571de2016-11-14 11:26:32 +01004717}
4718
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004719static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01004720{
4721 int err;
4722
Ido Schimmel9011b672017-05-16 19:38:25 +02004723 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01004724 return;
4725 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 +01004726 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004727 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004728 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
4729 if (err)
4730 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
4731}
4732
Ido Schimmel30572242016-12-03 16:45:01 +01004733struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01004734 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01004735 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02004736 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004737 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004738 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004739 struct fib_nh_notifier_info fnh_info;
4740 };
Ido Schimmel30572242016-12-03 16:45:01 +01004741 struct mlxsw_sp *mlxsw_sp;
4742 unsigned long event;
4743};
4744
Ido Schimmel66a57632017-08-03 13:28:26 +02004745static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004746{
Ido Schimmel30572242016-12-03 16:45:01 +01004747 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01004748 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01004749 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004750 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004751 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004752 int err;
4753
Ido Schimmel30572242016-12-03 16:45:01 +01004754 /* Protect internal structures from changes */
4755 rtnl_lock();
4756 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004757 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01004758 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004759 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004760 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01004761 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
4762 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004763 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004764 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004765 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01004766 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004767 break;
4768 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01004769 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
4770 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004771 break;
4772 case FIB_EVENT_RULE_ADD: /* fall through */
4773 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004774 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01004775 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004776 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004777 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004778 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01004779 case FIB_EVENT_NH_ADD: /* fall through */
4780 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02004781 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
4782 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01004783 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
4784 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004785 }
Ido Schimmel30572242016-12-03 16:45:01 +01004786 rtnl_unlock();
4787 kfree(fib_work);
4788}
4789
Ido Schimmel66a57632017-08-03 13:28:26 +02004790static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
4791{
Ido Schimmel583419f2017-08-03 13:28:27 +02004792 struct mlxsw_sp_fib_event_work *fib_work =
4793 container_of(work, struct mlxsw_sp_fib_event_work, work);
4794 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
4795 struct fib_rule *rule;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004796 bool replace;
Ido Schimmel428b8512017-08-03 13:28:28 +02004797 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02004798
4799 rtnl_lock();
4800 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004801 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004802 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004803 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel428b8512017-08-03 13:28:28 +02004804 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004805 fib_work->fen6_info.rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004806 if (err)
4807 mlxsw_sp_router_fib_abort(mlxsw_sp);
4808 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4809 break;
4810 case FIB_EVENT_ENTRY_DEL:
4811 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
4812 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4813 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004814 case FIB_EVENT_RULE_ADD: /* fall through */
4815 case FIB_EVENT_RULE_DEL:
4816 rule = fib_work->fr_info.rule;
4817 if (!fib6_rule_default(rule) && !rule->l3mdev)
4818 mlxsw_sp_router_fib_abort(mlxsw_sp);
4819 fib_rule_put(rule);
4820 break;
4821 }
4822 rtnl_unlock();
4823 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02004824}
4825
4826static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
4827 struct fib_notifier_info *info)
4828{
4829 switch (fib_work->event) {
4830 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4831 case FIB_EVENT_ENTRY_APPEND: /* fall through */
4832 case FIB_EVENT_ENTRY_ADD: /* fall through */
4833 case FIB_EVENT_ENTRY_DEL:
4834 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
4835 /* Take referece on fib_info to prevent it from being
4836 * freed while work is queued. Release it afterwards.
4837 */
4838 fib_info_hold(fib_work->fen_info.fi);
4839 break;
4840 case FIB_EVENT_RULE_ADD: /* fall through */
4841 case FIB_EVENT_RULE_DEL:
4842 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4843 fib_rule_get(fib_work->fr_info.rule);
4844 break;
4845 case FIB_EVENT_NH_ADD: /* fall through */
4846 case FIB_EVENT_NH_DEL:
4847 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
4848 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
4849 break;
4850 }
4851}
4852
4853static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
4854 struct fib_notifier_info *info)
4855{
Ido Schimmel583419f2017-08-03 13:28:27 +02004856 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004857 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004858 case FIB_EVENT_ENTRY_ADD: /* fall through */
4859 case FIB_EVENT_ENTRY_DEL:
4860 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
4861 rt6_hold(fib_work->fen6_info.rt);
4862 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004863 case FIB_EVENT_RULE_ADD: /* fall through */
4864 case FIB_EVENT_RULE_DEL:
4865 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4866 fib_rule_get(fib_work->fr_info.rule);
4867 break;
4868 }
Ido Schimmel66a57632017-08-03 13:28:26 +02004869}
4870
Ido Schimmel30572242016-12-03 16:45:01 +01004871/* Called with rcu_read_lock() */
4872static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
4873 unsigned long event, void *ptr)
4874{
Ido Schimmel30572242016-12-03 16:45:01 +01004875 struct mlxsw_sp_fib_event_work *fib_work;
4876 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02004877 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01004878
Ido Schimmel8e29f972017-09-15 15:31:07 +02004879 if (!net_eq(info->net, &init_net) ||
4880 (info->family != AF_INET && info->family != AF_INET6))
Ido Schimmel30572242016-12-03 16:45:01 +01004881 return NOTIFY_DONE;
4882
4883 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
4884 if (WARN_ON(!fib_work))
4885 return NOTIFY_BAD;
4886
Ido Schimmel7e39d112017-05-16 19:38:28 +02004887 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4888 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01004889 fib_work->event = event;
4890
Ido Schimmel66a57632017-08-03 13:28:26 +02004891 switch (info->family) {
4892 case AF_INET:
4893 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
4894 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01004895 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02004896 case AF_INET6:
4897 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
4898 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01004899 break;
Ido Schimmel30572242016-12-03 16:45:01 +01004900 }
4901
Ido Schimmela0e47612017-02-06 16:20:10 +01004902 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01004903
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004904 return NOTIFY_DONE;
4905}
4906
Ido Schimmel4724ba562017-03-10 08:53:39 +01004907static struct mlxsw_sp_rif *
4908mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
4909 const struct net_device *dev)
4910{
4911 int i;
4912
4913 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004914 if (mlxsw_sp->router->rifs[i] &&
4915 mlxsw_sp->router->rifs[i]->dev == dev)
4916 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004917
4918 return NULL;
4919}
4920
4921static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
4922{
4923 char ritr_pl[MLXSW_REG_RITR_LEN];
4924 int err;
4925
4926 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
4927 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4928 if (WARN_ON_ONCE(err))
4929 return err;
4930
4931 mlxsw_reg_ritr_enable_set(ritr_pl, false);
4932 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4933}
4934
4935static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004936 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004937{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004938 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
4939 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
4940 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004941}
4942
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004943static bool
4944mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
4945 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004946{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004947 struct inet6_dev *inet6_dev;
4948 bool addr_list_empty = true;
4949 struct in_device *idev;
4950
Ido Schimmel4724ba562017-03-10 08:53:39 +01004951 switch (event) {
4952 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02004953 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004954 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004955 idev = __in_dev_get_rtnl(dev);
4956 if (idev && idev->ifa_list)
4957 addr_list_empty = false;
4958
4959 inet6_dev = __in6_dev_get(dev);
4960 if (addr_list_empty && inet6_dev &&
4961 !list_empty(&inet6_dev->addr_list))
4962 addr_list_empty = false;
4963
4964 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004965 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004966 return true;
4967 /* It is possible we already removed the RIF ourselves
4968 * if it was assigned to a netdev that is now a bridge
4969 * or LAG slave.
4970 */
4971 return false;
4972 }
4973
4974 return false;
4975}
4976
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004977static enum mlxsw_sp_rif_type
4978mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
4979 const struct net_device *dev)
4980{
4981 enum mlxsw_sp_fid_type type;
4982
Petr Machata6ddb7422017-09-02 23:49:19 +02004983 if (mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL))
4984 return MLXSW_SP_RIF_TYPE_IPIP_LB;
4985
4986 /* Otherwise RIF type is derived from the type of the underlying FID. */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004987 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
4988 type = MLXSW_SP_FID_TYPE_8021Q;
4989 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
4990 type = MLXSW_SP_FID_TYPE_8021Q;
4991 else if (netif_is_bridge_master(dev))
4992 type = MLXSW_SP_FID_TYPE_8021D;
4993 else
4994 type = MLXSW_SP_FID_TYPE_RFID;
4995
4996 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
4997}
4998
Ido Schimmelde5ed992017-06-04 16:53:40 +02004999static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005000{
5001 int i;
5002
Ido Schimmelde5ed992017-06-04 16:53:40 +02005003 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
5004 if (!mlxsw_sp->router->rifs[i]) {
5005 *p_rif_index = i;
5006 return 0;
5007 }
5008 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01005009
Ido Schimmelde5ed992017-06-04 16:53:40 +02005010 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005011}
5012
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005013static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
5014 u16 vr_id,
5015 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005016{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005017 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005018
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005019 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005020 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005021 return NULL;
5022
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005023 INIT_LIST_HEAD(&rif->nexthop_list);
5024 INIT_LIST_HEAD(&rif->neigh_list);
5025 ether_addr_copy(rif->addr, l3_dev->dev_addr);
5026 rif->mtu = l3_dev->mtu;
5027 rif->vr_id = vr_id;
5028 rif->dev = l3_dev;
5029 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005030
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005031 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005032}
5033
Ido Schimmel5f9efff2017-05-16 19:38:27 +02005034struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
5035 u16 rif_index)
5036{
5037 return mlxsw_sp->router->rifs[rif_index];
5038}
5039
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02005040u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
5041{
5042 return rif->rif_index;
5043}
5044
Petr Machata92107cf2017-09-02 23:49:28 +02005045u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
5046{
5047 return lb_rif->common.rif_index;
5048}
5049
5050u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
5051{
5052 return lb_rif->ul_vr_id;
5053}
5054
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02005055int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
5056{
5057 return rif->dev->ifindex;
5058}
5059
Yotam Gigi91e4d592017-09-19 10:00:19 +02005060const struct net_device *mlxsw_sp_rif_dev(const struct mlxsw_sp_rif *rif)
5061{
5062 return rif->dev;
5063}
5064
Ido Schimmel4724ba562017-03-10 08:53:39 +01005065static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005066mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
5067 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005068{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005069 u32 tb_id = l3mdev_fib_table(params->dev);
5070 const struct mlxsw_sp_rif_ops *ops;
Petr Machata010cadf2017-09-02 23:49:18 +02005071 struct mlxsw_sp_fid *fid = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005072 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005073 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005074 struct mlxsw_sp_vr *vr;
5075 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005076 int err;
5077
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005078 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
5079 ops = mlxsw_sp->router->rif_ops_arr[type];
5080
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005081 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
5082 if (IS_ERR(vr))
5083 return ERR_CAST(vr);
5084
Ido Schimmelde5ed992017-06-04 16:53:40 +02005085 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
5086 if (err)
5087 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005088
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005089 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02005090 if (!rif) {
5091 err = -ENOMEM;
5092 goto err_rif_alloc;
5093 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005094 rif->mlxsw_sp = mlxsw_sp;
5095 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02005096
Petr Machata010cadf2017-09-02 23:49:18 +02005097 if (ops->fid_get) {
5098 fid = ops->fid_get(rif);
5099 if (IS_ERR(fid)) {
5100 err = PTR_ERR(fid);
5101 goto err_fid_get;
5102 }
5103 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02005104 }
5105
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005106 if (ops->setup)
5107 ops->setup(rif, params);
5108
5109 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005110 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005111 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005112
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005113 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02005114 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel69132292017-03-10 08:53:42 +01005115 vr->rif_count++;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005116
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005117 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005118
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005119err_configure:
Petr Machata010cadf2017-09-02 23:49:18 +02005120 if (fid)
5121 mlxsw_sp_fid_put(fid);
Ido Schimmela1107482017-05-26 08:37:39 +02005122err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005123 kfree(rif);
5124err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02005125err_rif_index_alloc:
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005126 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005127 return ERR_PTR(err);
5128}
5129
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005130void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005131{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005132 const struct mlxsw_sp_rif_ops *ops = rif->ops;
5133 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02005134 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005135 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005136
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005137 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005138 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02005139
Ido Schimmel69132292017-03-10 08:53:42 +01005140 vr->rif_count--;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005141 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005142 mlxsw_sp_rif_counters_free(rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005143 ops->deconfigure(rif);
Petr Machata010cadf2017-09-02 23:49:18 +02005144 if (fid)
5145 /* Loopback RIFs are not associated with a FID. */
5146 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005147 kfree(rif);
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005148 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005149}
5150
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005151static void
5152mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
5153 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
5154{
5155 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
5156
5157 params->vid = mlxsw_sp_port_vlan->vid;
5158 params->lag = mlxsw_sp_port->lagged;
5159 if (params->lag)
5160 params->lag_id = mlxsw_sp_port->lag_id;
5161 else
5162 params->system_port = mlxsw_sp_port->local_port;
5163}
5164
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005165static int
Ido Schimmela1107482017-05-26 08:37:39 +02005166mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005167 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005168{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005169 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02005170 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005171 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005172 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005173 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005174 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005175
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02005176 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005177 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005178 struct mlxsw_sp_rif_params params = {
5179 .dev = l3_dev,
5180 };
5181
5182 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
5183 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005184 if (IS_ERR(rif))
5185 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005186 }
5187
Ido Schimmela1107482017-05-26 08:37:39 +02005188 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005189 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02005190 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
5191 if (err)
5192 goto err_fid_port_vid_map;
5193
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005194 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005195 if (err)
5196 goto err_port_vid_learning_set;
5197
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005198 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005199 BR_STATE_FORWARDING);
5200 if (err)
5201 goto err_port_vid_stp_set;
5202
Ido Schimmela1107482017-05-26 08:37:39 +02005203 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005204
Ido Schimmel4724ba562017-03-10 08:53:39 +01005205 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005206
5207err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005208 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005209err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02005210 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
5211err_fid_port_vid_map:
5212 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005213 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005214}
5215
Ido Schimmela1107482017-05-26 08:37:39 +02005216void
5217mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005218{
Ido Schimmelce95e152017-05-26 08:37:27 +02005219 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005220 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02005221 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02005222
Ido Schimmela1107482017-05-26 08:37:39 +02005223 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
5224 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02005225
Ido Schimmela1107482017-05-26 08:37:39 +02005226 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005227 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
5228 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02005229 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
5230 /* If router port holds the last reference on the rFID, then the
5231 * associated Sub-port RIF will be destroyed.
5232 */
5233 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005234}
5235
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005236static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
5237 struct net_device *port_dev,
5238 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005239{
5240 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02005241 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005242
Ido Schimmelce95e152017-05-26 08:37:27 +02005243 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005244 if (WARN_ON(!mlxsw_sp_port_vlan))
5245 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005246
5247 switch (event) {
5248 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02005249 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005250 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005251 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02005252 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005253 break;
5254 }
5255
5256 return 0;
5257}
5258
5259static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
5260 unsigned long event)
5261{
Jiri Pirko2b94e582017-04-18 16:55:37 +02005262 if (netif_is_bridge_port(port_dev) ||
5263 netif_is_lag_port(port_dev) ||
5264 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01005265 return 0;
5266
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005267 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005268}
5269
5270static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
5271 struct net_device *lag_dev,
5272 unsigned long event, u16 vid)
5273{
5274 struct net_device *port_dev;
5275 struct list_head *iter;
5276 int err;
5277
5278 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
5279 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005280 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
5281 port_dev,
5282 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005283 if (err)
5284 return err;
5285 }
5286 }
5287
5288 return 0;
5289}
5290
5291static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
5292 unsigned long event)
5293{
5294 if (netif_is_bridge_port(lag_dev))
5295 return 0;
5296
5297 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
5298}
5299
Ido Schimmel4724ba562017-03-10 08:53:39 +01005300static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01005301 unsigned long event)
5302{
5303 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005304 struct mlxsw_sp_rif_params params = {
5305 .dev = l3_dev,
5306 };
Ido Schimmela1107482017-05-26 08:37:39 +02005307 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005308
5309 switch (event) {
5310 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005311 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
5312 if (IS_ERR(rif))
5313 return PTR_ERR(rif);
5314 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005315 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02005316 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005317 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005318 break;
5319 }
5320
5321 return 0;
5322}
5323
5324static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
5325 unsigned long event)
5326{
5327 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005328 u16 vid = vlan_dev_vlan_id(vlan_dev);
5329
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03005330 if (netif_is_bridge_port(vlan_dev))
5331 return 0;
5332
Ido Schimmel4724ba562017-03-10 08:53:39 +01005333 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005334 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
5335 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005336 else if (netif_is_lag_master(real_dev))
5337 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
5338 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02005339 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02005340 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005341
5342 return 0;
5343}
5344
Ido Schimmelb1e45522017-04-30 19:47:14 +03005345static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
5346 unsigned long event)
5347{
5348 if (mlxsw_sp_port_dev_check(dev))
5349 return mlxsw_sp_inetaddr_port_event(dev, event);
5350 else if (netif_is_lag_master(dev))
5351 return mlxsw_sp_inetaddr_lag_event(dev, event);
5352 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02005353 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03005354 else if (is_vlan_dev(dev))
5355 return mlxsw_sp_inetaddr_vlan_event(dev, event);
5356 else
5357 return 0;
5358}
5359
Ido Schimmel4724ba562017-03-10 08:53:39 +01005360int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
5361 unsigned long event, void *ptr)
5362{
5363 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
5364 struct net_device *dev = ifa->ifa_dev->dev;
5365 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005366 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005367 int err = 0;
5368
5369 mlxsw_sp = mlxsw_sp_lower_get(dev);
5370 if (!mlxsw_sp)
5371 goto out;
5372
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005373 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005374 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01005375 goto out;
5376
Ido Schimmelb1e45522017-04-30 19:47:14 +03005377 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005378out:
5379 return notifier_from_errno(err);
5380}
5381
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005382struct mlxsw_sp_inet6addr_event_work {
5383 struct work_struct work;
5384 struct net_device *dev;
5385 unsigned long event;
5386};
5387
5388static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
5389{
5390 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
5391 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
5392 struct net_device *dev = inet6addr_work->dev;
5393 unsigned long event = inet6addr_work->event;
5394 struct mlxsw_sp *mlxsw_sp;
5395 struct mlxsw_sp_rif *rif;
5396
5397 rtnl_lock();
5398 mlxsw_sp = mlxsw_sp_lower_get(dev);
5399 if (!mlxsw_sp)
5400 goto out;
5401
5402 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5403 if (!mlxsw_sp_rif_should_config(rif, dev, event))
5404 goto out;
5405
5406 __mlxsw_sp_inetaddr_event(dev, event);
5407out:
5408 rtnl_unlock();
5409 dev_put(dev);
5410 kfree(inet6addr_work);
5411}
5412
5413/* Called with rcu_read_lock() */
5414int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
5415 unsigned long event, void *ptr)
5416{
5417 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
5418 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
5419 struct net_device *dev = if6->idev->dev;
5420
5421 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
5422 return NOTIFY_DONE;
5423
5424 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
5425 if (!inet6addr_work)
5426 return NOTIFY_BAD;
5427
5428 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
5429 inet6addr_work->dev = dev;
5430 inet6addr_work->event = event;
5431 dev_hold(dev);
5432 mlxsw_core_schedule_work(&inet6addr_work->work);
5433
5434 return NOTIFY_DONE;
5435}
5436
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005437static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01005438 const char *mac, int mtu)
5439{
5440 char ritr_pl[MLXSW_REG_RITR_LEN];
5441 int err;
5442
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005443 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005444 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5445 if (err)
5446 return err;
5447
5448 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
5449 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
5450 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
5451 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5452}
5453
5454int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
5455{
5456 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005457 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005458 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005459 int err;
5460
5461 mlxsw_sp = mlxsw_sp_lower_get(dev);
5462 if (!mlxsw_sp)
5463 return 0;
5464
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005465 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5466 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005467 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02005468 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005469
Ido Schimmela1107482017-05-26 08:37:39 +02005470 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005471 if (err)
5472 return err;
5473
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005474 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
5475 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005476 if (err)
5477 goto err_rif_edit;
5478
Ido Schimmela1107482017-05-26 08:37:39 +02005479 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005480 if (err)
5481 goto err_rif_fdb_op;
5482
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005483 ether_addr_copy(rif->addr, dev->dev_addr);
5484 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005485
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005486 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005487
5488 return 0;
5489
5490err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005491 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005492err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02005493 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005494 return err;
5495}
5496
Ido Schimmelb1e45522017-04-30 19:47:14 +03005497static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
5498 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005499{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005500 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01005501
Ido Schimmelb1e45522017-04-30 19:47:14 +03005502 /* If netdev is already associated with a RIF, then we need to
5503 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01005504 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03005505 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
5506 if (rif)
5507 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005508
Ido Schimmelb1e45522017-04-30 19:47:14 +03005509 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005510}
5511
Ido Schimmelb1e45522017-04-30 19:47:14 +03005512static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
5513 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005514{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005515 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01005516
Ido Schimmelb1e45522017-04-30 19:47:14 +03005517 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
5518 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005519 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03005520 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005521}
5522
Ido Schimmelb1e45522017-04-30 19:47:14 +03005523int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
5524 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005525{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005526 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
5527 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005528
Ido Schimmelb1e45522017-04-30 19:47:14 +03005529 if (!mlxsw_sp)
5530 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005531
Ido Schimmelb1e45522017-04-30 19:47:14 +03005532 switch (event) {
5533 case NETDEV_PRECHANGEUPPER:
5534 return 0;
5535 case NETDEV_CHANGEUPPER:
5536 if (info->linking)
5537 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
5538 else
5539 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
5540 break;
5541 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005542
Ido Schimmelb1e45522017-04-30 19:47:14 +03005543 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005544}
5545
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005546static struct mlxsw_sp_rif_subport *
5547mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02005548{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005549 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02005550}
5551
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005552static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
5553 const struct mlxsw_sp_rif_params *params)
5554{
5555 struct mlxsw_sp_rif_subport *rif_subport;
5556
5557 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5558 rif_subport->vid = params->vid;
5559 rif_subport->lag = params->lag;
5560 if (params->lag)
5561 rif_subport->lag_id = params->lag_id;
5562 else
5563 rif_subport->system_port = params->system_port;
5564}
5565
5566static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
5567{
5568 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5569 struct mlxsw_sp_rif_subport *rif_subport;
5570 char ritr_pl[MLXSW_REG_RITR_LEN];
5571
5572 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5573 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
Petr Machata9571e822017-09-02 23:49:14 +02005574 rif->rif_index, rif->vr_id, rif->dev->mtu);
5575 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005576 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
5577 rif_subport->lag ? rif_subport->lag_id :
5578 rif_subport->system_port,
5579 rif_subport->vid);
5580
5581 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5582}
5583
5584static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
5585{
Petr Machata010cadf2017-09-02 23:49:18 +02005586 int err;
5587
5588 err = mlxsw_sp_rif_subport_op(rif, true);
5589 if (err)
5590 return err;
5591
5592 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5593 mlxsw_sp_fid_index(rif->fid), true);
5594 if (err)
5595 goto err_rif_fdb_op;
5596
5597 mlxsw_sp_fid_rif_set(rif->fid, rif);
5598 return 0;
5599
5600err_rif_fdb_op:
5601 mlxsw_sp_rif_subport_op(rif, false);
5602 return err;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005603}
5604
5605static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
5606{
Petr Machata010cadf2017-09-02 23:49:18 +02005607 struct mlxsw_sp_fid *fid = rif->fid;
5608
5609 mlxsw_sp_fid_rif_set(fid, NULL);
5610 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5611 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005612 mlxsw_sp_rif_subport_op(rif, false);
5613}
5614
5615static struct mlxsw_sp_fid *
5616mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
5617{
5618 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
5619}
5620
5621static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
5622 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
5623 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
5624 .setup = mlxsw_sp_rif_subport_setup,
5625 .configure = mlxsw_sp_rif_subport_configure,
5626 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
5627 .fid_get = mlxsw_sp_rif_subport_fid_get,
5628};
5629
5630static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
5631 enum mlxsw_reg_ritr_if_type type,
5632 u16 vid_fid, bool enable)
5633{
5634 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5635 char ritr_pl[MLXSW_REG_RITR_LEN];
5636
5637 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
Petr Machata9571e822017-09-02 23:49:14 +02005638 rif->dev->mtu);
5639 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005640 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
5641
5642 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5643}
5644
5645static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
5646{
5647 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
5648}
5649
5650static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
5651{
5652 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5653 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
5654 int err;
5655
5656 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
5657 if (err)
5658 return err;
5659
Ido Schimmel0d284812017-07-18 10:10:12 +02005660 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5661 mlxsw_sp_router_port(mlxsw_sp), true);
5662 if (err)
5663 goto err_fid_mc_flood_set;
5664
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005665 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5666 mlxsw_sp_router_port(mlxsw_sp), true);
5667 if (err)
5668 goto err_fid_bc_flood_set;
5669
Petr Machata010cadf2017-09-02 23:49:18 +02005670 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5671 mlxsw_sp_fid_index(rif->fid), true);
5672 if (err)
5673 goto err_rif_fdb_op;
5674
5675 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005676 return 0;
5677
Petr Machata010cadf2017-09-02 23:49:18 +02005678err_rif_fdb_op:
5679 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5680 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005681err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005682 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5683 mlxsw_sp_router_port(mlxsw_sp), false);
5684err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005685 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5686 return err;
5687}
5688
5689static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
5690{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005691 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02005692 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5693 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005694
Petr Machata010cadf2017-09-02 23:49:18 +02005695 mlxsw_sp_fid_rif_set(fid, NULL);
5696 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5697 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005698 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5699 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005700 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5701 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005702 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5703}
5704
5705static struct mlxsw_sp_fid *
5706mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
5707{
5708 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
5709
5710 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
5711}
5712
5713static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
5714 .type = MLXSW_SP_RIF_TYPE_VLAN,
5715 .rif_size = sizeof(struct mlxsw_sp_rif),
5716 .configure = mlxsw_sp_rif_vlan_configure,
5717 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
5718 .fid_get = mlxsw_sp_rif_vlan_fid_get,
5719};
5720
5721static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
5722{
5723 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5724 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
5725 int err;
5726
5727 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
5728 true);
5729 if (err)
5730 return err;
5731
Ido Schimmel0d284812017-07-18 10:10:12 +02005732 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5733 mlxsw_sp_router_port(mlxsw_sp), true);
5734 if (err)
5735 goto err_fid_mc_flood_set;
5736
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005737 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5738 mlxsw_sp_router_port(mlxsw_sp), true);
5739 if (err)
5740 goto err_fid_bc_flood_set;
5741
Petr Machata010cadf2017-09-02 23:49:18 +02005742 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5743 mlxsw_sp_fid_index(rif->fid), true);
5744 if (err)
5745 goto err_rif_fdb_op;
5746
5747 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005748 return 0;
5749
Petr Machata010cadf2017-09-02 23:49:18 +02005750err_rif_fdb_op:
5751 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5752 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005753err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005754 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5755 mlxsw_sp_router_port(mlxsw_sp), false);
5756err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005757 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5758 return err;
5759}
5760
5761static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
5762{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005763 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02005764 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5765 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005766
Petr Machata010cadf2017-09-02 23:49:18 +02005767 mlxsw_sp_fid_rif_set(fid, NULL);
5768 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5769 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005770 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5771 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005772 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5773 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005774 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5775}
5776
5777static struct mlxsw_sp_fid *
5778mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
5779{
5780 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
5781}
5782
5783static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
5784 .type = MLXSW_SP_RIF_TYPE_FID,
5785 .rif_size = sizeof(struct mlxsw_sp_rif),
5786 .configure = mlxsw_sp_rif_fid_configure,
5787 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
5788 .fid_get = mlxsw_sp_rif_fid_fid_get,
5789};
5790
Petr Machata6ddb7422017-09-02 23:49:19 +02005791static struct mlxsw_sp_rif_ipip_lb *
5792mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif)
5793{
5794 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
5795}
5796
5797static void
5798mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif,
5799 const struct mlxsw_sp_rif_params *params)
5800{
5801 struct mlxsw_sp_rif_params_ipip_lb *params_lb;
5802 struct mlxsw_sp_rif_ipip_lb *rif_lb;
5803
5804 params_lb = container_of(params, struct mlxsw_sp_rif_params_ipip_lb,
5805 common);
5806 rif_lb = mlxsw_sp_rif_ipip_lb_rif(rif);
5807 rif_lb->lb_config = params_lb->lb_config;
5808}
5809
5810static int
5811mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif,
5812 struct mlxsw_sp_vr *ul_vr, bool enable)
5813{
5814 struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config;
5815 struct mlxsw_sp_rif *rif = &lb_rif->common;
5816 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5817 char ritr_pl[MLXSW_REG_RITR_LEN];
5818 u32 saddr4;
5819
5820 switch (lb_cf.ul_protocol) {
5821 case MLXSW_SP_L3_PROTO_IPV4:
5822 saddr4 = be32_to_cpu(lb_cf.saddr.addr4);
5823 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,
5824 rif->rif_index, rif->vr_id, rif->dev->mtu);
5825 mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt,
5826 MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET,
5827 ul_vr->id, saddr4, lb_cf.okey);
5828 break;
5829
5830 case MLXSW_SP_L3_PROTO_IPV6:
5831 return -EAFNOSUPPORT;
5832 }
5833
5834 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5835}
5836
5837static int
5838mlxsw_sp_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif)
5839{
5840 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
5841 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev);
5842 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5843 struct mlxsw_sp_vr *ul_vr;
5844 int err;
5845
5846 ul_vr = mlxsw_sp_vr_get(mlxsw_sp, ul_tb_id);
5847 if (IS_ERR(ul_vr))
5848 return PTR_ERR(ul_vr);
5849
5850 err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, true);
5851 if (err)
5852 goto err_loopback_op;
5853
5854 lb_rif->ul_vr_id = ul_vr->id;
5855 ++ul_vr->rif_count;
5856 return 0;
5857
5858err_loopback_op:
5859 mlxsw_sp_vr_put(ul_vr);
5860 return err;
5861}
5862
5863static void mlxsw_sp_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif)
5864{
5865 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
5866 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5867 struct mlxsw_sp_vr *ul_vr;
5868
5869 ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id];
5870 mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, false);
5871
5872 --ul_vr->rif_count;
5873 mlxsw_sp_vr_put(ul_vr);
5874}
5875
5876static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = {
5877 .type = MLXSW_SP_RIF_TYPE_IPIP_LB,
5878 .rif_size = sizeof(struct mlxsw_sp_rif_ipip_lb),
5879 .setup = mlxsw_sp_rif_ipip_lb_setup,
5880 .configure = mlxsw_sp_rif_ipip_lb_configure,
5881 .deconfigure = mlxsw_sp_rif_ipip_lb_deconfigure,
5882};
5883
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005884static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
5885 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
5886 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
5887 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
Petr Machata6ddb7422017-09-02 23:49:19 +02005888 [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp_rif_ipip_lb_ops,
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005889};
5890
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005891static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
5892{
5893 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
5894
5895 mlxsw_sp->router->rifs = kcalloc(max_rifs,
5896 sizeof(struct mlxsw_sp_rif *),
5897 GFP_KERNEL);
5898 if (!mlxsw_sp->router->rifs)
5899 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005900
5901 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
5902
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005903 return 0;
5904}
5905
5906static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
5907{
5908 int i;
5909
5910 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
5911 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
5912
5913 kfree(mlxsw_sp->router->rifs);
5914}
5915
Petr Machata38ebc0f2017-09-02 23:49:17 +02005916static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp)
5917{
5918 mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr;
Petr Machata1012b9a2017-09-02 23:49:23 +02005919 INIT_LIST_HEAD(&mlxsw_sp->router->ipip_list);
Petr Machata38ebc0f2017-09-02 23:49:17 +02005920 return 0;
5921}
5922
5923static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp)
5924{
Petr Machata1012b9a2017-09-02 23:49:23 +02005925 WARN_ON(!list_empty(&mlxsw_sp->router->ipip_list));
Petr Machata38ebc0f2017-09-02 23:49:17 +02005926}
5927
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005928static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
5929{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005930 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005931
5932 /* Flush pending FIB notifications and then flush the device's
5933 * table before requesting another dump. The FIB notification
5934 * block is unregistered, so no need to take RTNL.
5935 */
5936 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02005937 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
5938 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005939}
5940
Ido Schimmel4724ba562017-03-10 08:53:39 +01005941static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5942{
5943 char rgcr_pl[MLXSW_REG_RGCR_LEN];
5944 u64 max_rifs;
5945 int err;
5946
5947 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
5948 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005949 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005950
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005951 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005952 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
5953 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
5954 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005955 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005956 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005957}
5958
5959static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5960{
5961 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01005962
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005963 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005964 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005965}
5966
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005967int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5968{
Ido Schimmel9011b672017-05-16 19:38:25 +02005969 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005970 int err;
5971
Ido Schimmel9011b672017-05-16 19:38:25 +02005972 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
5973 if (!router)
5974 return -ENOMEM;
5975 mlxsw_sp->router = router;
5976 router->mlxsw_sp = mlxsw_sp;
5977
5978 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005979 err = __mlxsw_sp_router_init(mlxsw_sp);
5980 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02005981 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005982
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005983 err = mlxsw_sp_rifs_init(mlxsw_sp);
5984 if (err)
5985 goto err_rifs_init;
5986
Petr Machata38ebc0f2017-09-02 23:49:17 +02005987 err = mlxsw_sp_ipips_init(mlxsw_sp);
5988 if (err)
5989 goto err_ipips_init;
5990
Ido Schimmel9011b672017-05-16 19:38:25 +02005991 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005992 &mlxsw_sp_nexthop_ht_params);
5993 if (err)
5994 goto err_nexthop_ht_init;
5995
Ido Schimmel9011b672017-05-16 19:38:25 +02005996 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005997 &mlxsw_sp_nexthop_group_ht_params);
5998 if (err)
5999 goto err_nexthop_group_ht_init;
6000
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02006001 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_list);
Ido Schimmel8494ab02017-03-24 08:02:47 +01006002 err = mlxsw_sp_lpm_init(mlxsw_sp);
6003 if (err)
6004 goto err_lpm_init;
6005
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006006 err = mlxsw_sp_vrs_init(mlxsw_sp);
6007 if (err)
6008 goto err_vrs_init;
6009
Ido Schimmel8c9583a2016-10-27 15:12:57 +02006010 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006011 if (err)
6012 goto err_neigh_init;
6013
Ido Schimmel7e39d112017-05-16 19:38:28 +02006014 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
6015 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006016 mlxsw_sp_router_fib_dump_flush);
6017 if (err)
6018 goto err_register_fib_notifier;
6019
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006020 return 0;
6021
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006022err_register_fib_notifier:
6023 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006024err_neigh_init:
6025 mlxsw_sp_vrs_fini(mlxsw_sp);
6026err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01006027 mlxsw_sp_lpm_fini(mlxsw_sp);
6028err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02006029 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01006030err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02006031 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01006032err_nexthop_ht_init:
Petr Machata38ebc0f2017-09-02 23:49:17 +02006033 mlxsw_sp_ipips_fini(mlxsw_sp);
6034err_ipips_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006035 mlxsw_sp_rifs_fini(mlxsw_sp);
6036err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006037 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006038err_router_init:
6039 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006040 return err;
6041}
6042
6043void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
6044{
Ido Schimmel7e39d112017-05-16 19:38:28 +02006045 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006046 mlxsw_sp_neigh_fini(mlxsw_sp);
6047 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01006048 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006049 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
6050 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Petr Machata38ebc0f2017-09-02 23:49:17 +02006051 mlxsw_sp_ipips_fini(mlxsw_sp);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006052 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006053 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006054 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006055}