18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (c) 2016-2018 Mellanox Technologies. All rights reserved */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/kernel.h>
58c2ecf20Sopenharmony_ci#include <linux/types.h>
68c2ecf20Sopenharmony_ci#include <linux/rhashtable.h>
78c2ecf20Sopenharmony_ci#include <linux/bitops.h>
88c2ecf20Sopenharmony_ci#include <linux/in6.h>
98c2ecf20Sopenharmony_ci#include <linux/notifier.h>
108c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
118c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
128c2ecf20Sopenharmony_ci#include <linux/if_bridge.h>
138c2ecf20Sopenharmony_ci#include <linux/socket.h>
148c2ecf20Sopenharmony_ci#include <linux/route.h>
158c2ecf20Sopenharmony_ci#include <linux/gcd.h>
168c2ecf20Sopenharmony_ci#include <linux/if_macvlan.h>
178c2ecf20Sopenharmony_ci#include <linux/refcount.h>
188c2ecf20Sopenharmony_ci#include <linux/jhash.h>
198c2ecf20Sopenharmony_ci#include <linux/net_namespace.h>
208c2ecf20Sopenharmony_ci#include <linux/mutex.h>
218c2ecf20Sopenharmony_ci#include <net/netevent.h>
228c2ecf20Sopenharmony_ci#include <net/neighbour.h>
238c2ecf20Sopenharmony_ci#include <net/arp.h>
248c2ecf20Sopenharmony_ci#include <net/ip_fib.h>
258c2ecf20Sopenharmony_ci#include <net/ip6_fib.h>
268c2ecf20Sopenharmony_ci#include <net/nexthop.h>
278c2ecf20Sopenharmony_ci#include <net/fib_rules.h>
288c2ecf20Sopenharmony_ci#include <net/ip_tunnels.h>
298c2ecf20Sopenharmony_ci#include <net/l3mdev.h>
308c2ecf20Sopenharmony_ci#include <net/addrconf.h>
318c2ecf20Sopenharmony_ci#include <net/ndisc.h>
328c2ecf20Sopenharmony_ci#include <net/ipv6.h>
338c2ecf20Sopenharmony_ci#include <net/fib_notifier.h>
348c2ecf20Sopenharmony_ci#include <net/switchdev.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#include "spectrum.h"
378c2ecf20Sopenharmony_ci#include "core.h"
388c2ecf20Sopenharmony_ci#include "reg.h"
398c2ecf20Sopenharmony_ci#include "spectrum_cnt.h"
408c2ecf20Sopenharmony_ci#include "spectrum_dpipe.h"
418c2ecf20Sopenharmony_ci#include "spectrum_ipip.h"
428c2ecf20Sopenharmony_ci#include "spectrum_mr.h"
438c2ecf20Sopenharmony_ci#include "spectrum_mr_tcam.h"
448c2ecf20Sopenharmony_ci#include "spectrum_router.h"
458c2ecf20Sopenharmony_ci#include "spectrum_span.h"
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistruct mlxsw_sp_fib;
488c2ecf20Sopenharmony_cistruct mlxsw_sp_vr;
498c2ecf20Sopenharmony_cistruct mlxsw_sp_lpm_tree;
508c2ecf20Sopenharmony_cistruct mlxsw_sp_rif_ops;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistruct mlxsw_sp_rif {
538c2ecf20Sopenharmony_ci	struct list_head nexthop_list;
548c2ecf20Sopenharmony_ci	struct list_head neigh_list;
558c2ecf20Sopenharmony_ci	struct net_device *dev; /* NULL for underlay RIF */
568c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid;
578c2ecf20Sopenharmony_ci	unsigned char addr[ETH_ALEN];
588c2ecf20Sopenharmony_ci	int mtu;
598c2ecf20Sopenharmony_ci	u16 rif_index;
608c2ecf20Sopenharmony_ci	u16 vr_id;
618c2ecf20Sopenharmony_ci	const struct mlxsw_sp_rif_ops *ops;
628c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	unsigned int counter_ingress;
658c2ecf20Sopenharmony_ci	bool counter_ingress_valid;
668c2ecf20Sopenharmony_ci	unsigned int counter_egress;
678c2ecf20Sopenharmony_ci	bool counter_egress_valid;
688c2ecf20Sopenharmony_ci};
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistruct mlxsw_sp_rif_params {
718c2ecf20Sopenharmony_ci	struct net_device *dev;
728c2ecf20Sopenharmony_ci	union {
738c2ecf20Sopenharmony_ci		u16 system_port;
748c2ecf20Sopenharmony_ci		u16 lag_id;
758c2ecf20Sopenharmony_ci	};
768c2ecf20Sopenharmony_ci	u16 vid;
778c2ecf20Sopenharmony_ci	bool lag;
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistruct mlxsw_sp_rif_subport {
818c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif common;
828c2ecf20Sopenharmony_ci	refcount_t ref_count;
838c2ecf20Sopenharmony_ci	union {
848c2ecf20Sopenharmony_ci		u16 system_port;
858c2ecf20Sopenharmony_ci		u16 lag_id;
868c2ecf20Sopenharmony_ci	};
878c2ecf20Sopenharmony_ci	u16 vid;
888c2ecf20Sopenharmony_ci	bool lag;
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistruct mlxsw_sp_rif_ipip_lb {
928c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif common;
938c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_ipip_lb_config lb_config;
948c2ecf20Sopenharmony_ci	u16 ul_vr_id; /* Reserved for Spectrum-2. */
958c2ecf20Sopenharmony_ci	u16 ul_rif_id; /* Reserved for Spectrum. */
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistruct mlxsw_sp_rif_params_ipip_lb {
998c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_params common;
1008c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_ipip_lb_config lb_config;
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistruct mlxsw_sp_rif_ops {
1048c2ecf20Sopenharmony_ci	enum mlxsw_sp_rif_type type;
1058c2ecf20Sopenharmony_ci	size_t rif_size;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	void (*setup)(struct mlxsw_sp_rif *rif,
1088c2ecf20Sopenharmony_ci		      const struct mlxsw_sp_rif_params *params);
1098c2ecf20Sopenharmony_ci	int (*configure)(struct mlxsw_sp_rif *rif);
1108c2ecf20Sopenharmony_ci	void (*deconfigure)(struct mlxsw_sp_rif *rif);
1118c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif,
1128c2ecf20Sopenharmony_ci					 struct netlink_ext_ack *extack);
1138c2ecf20Sopenharmony_ci	void (*fdb_del)(struct mlxsw_sp_rif *rif, const char *mac);
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic struct mlxsw_sp_rif *
1178c2ecf20Sopenharmony_cimlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
1188c2ecf20Sopenharmony_ci			 const struct net_device *dev);
1198c2ecf20Sopenharmony_cistatic void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif);
1208c2ecf20Sopenharmony_cistatic void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree);
1218c2ecf20Sopenharmony_cistatic void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
1228c2ecf20Sopenharmony_ci				  struct mlxsw_sp_lpm_tree *lpm_tree);
1238c2ecf20Sopenharmony_cistatic int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
1248c2ecf20Sopenharmony_ci				     const struct mlxsw_sp_fib *fib,
1258c2ecf20Sopenharmony_ci				     u8 tree_id);
1268c2ecf20Sopenharmony_cistatic int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
1278c2ecf20Sopenharmony_ci				       const struct mlxsw_sp_fib *fib);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic unsigned int *
1308c2ecf20Sopenharmony_cimlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
1318c2ecf20Sopenharmony_ci			   enum mlxsw_sp_rif_counter_dir dir)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	switch (dir) {
1348c2ecf20Sopenharmony_ci	case MLXSW_SP_RIF_COUNTER_EGRESS:
1358c2ecf20Sopenharmony_ci		return &rif->counter_egress;
1368c2ecf20Sopenharmony_ci	case MLXSW_SP_RIF_COUNTER_INGRESS:
1378c2ecf20Sopenharmony_ci		return &rif->counter_ingress;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci	return NULL;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic bool
1438c2ecf20Sopenharmony_cimlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
1448c2ecf20Sopenharmony_ci			       enum mlxsw_sp_rif_counter_dir dir)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	switch (dir) {
1478c2ecf20Sopenharmony_ci	case MLXSW_SP_RIF_COUNTER_EGRESS:
1488c2ecf20Sopenharmony_ci		return rif->counter_egress_valid;
1498c2ecf20Sopenharmony_ci	case MLXSW_SP_RIF_COUNTER_INGRESS:
1508c2ecf20Sopenharmony_ci		return rif->counter_ingress_valid;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci	return false;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic void
1568c2ecf20Sopenharmony_cimlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
1578c2ecf20Sopenharmony_ci			       enum mlxsw_sp_rif_counter_dir dir,
1588c2ecf20Sopenharmony_ci			       bool valid)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	switch (dir) {
1618c2ecf20Sopenharmony_ci	case MLXSW_SP_RIF_COUNTER_EGRESS:
1628c2ecf20Sopenharmony_ci		rif->counter_egress_valid = valid;
1638c2ecf20Sopenharmony_ci		break;
1648c2ecf20Sopenharmony_ci	case MLXSW_SP_RIF_COUNTER_INGRESS:
1658c2ecf20Sopenharmony_ci		rif->counter_ingress_valid = valid;
1668c2ecf20Sopenharmony_ci		break;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
1718c2ecf20Sopenharmony_ci				     unsigned int counter_index, bool enable,
1728c2ecf20Sopenharmony_ci				     enum mlxsw_sp_rif_counter_dir dir)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	char ritr_pl[MLXSW_REG_RITR_LEN];
1758c2ecf20Sopenharmony_ci	bool is_egress = false;
1768c2ecf20Sopenharmony_ci	int err;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
1798c2ecf20Sopenharmony_ci		is_egress = true;
1808c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
1818c2ecf20Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
1828c2ecf20Sopenharmony_ci	if (err)
1838c2ecf20Sopenharmony_ci		return err;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
1868c2ecf20Sopenharmony_ci				    is_egress);
1878c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ciint mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
1918c2ecf20Sopenharmony_ci				   struct mlxsw_sp_rif *rif,
1928c2ecf20Sopenharmony_ci				   enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	char ricnt_pl[MLXSW_REG_RICNT_LEN];
1958c2ecf20Sopenharmony_ci	unsigned int *p_counter_index;
1968c2ecf20Sopenharmony_ci	bool valid;
1978c2ecf20Sopenharmony_ci	int err;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
2008c2ecf20Sopenharmony_ci	if (!valid)
2018c2ecf20Sopenharmony_ci		return -EINVAL;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
2048c2ecf20Sopenharmony_ci	if (!p_counter_index)
2058c2ecf20Sopenharmony_ci		return -EINVAL;
2068c2ecf20Sopenharmony_ci	mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
2078c2ecf20Sopenharmony_ci			     MLXSW_REG_RICNT_OPCODE_NOP);
2088c2ecf20Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
2098c2ecf20Sopenharmony_ci	if (err)
2108c2ecf20Sopenharmony_ci		return err;
2118c2ecf20Sopenharmony_ci	*cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
2128c2ecf20Sopenharmony_ci	return 0;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
2168c2ecf20Sopenharmony_ci				      unsigned int counter_index)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	char ricnt_pl[MLXSW_REG_RICNT_LEN];
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
2218c2ecf20Sopenharmony_ci			     MLXSW_REG_RICNT_OPCODE_CLEAR);
2228c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ciint mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
2268c2ecf20Sopenharmony_ci			       struct mlxsw_sp_rif *rif,
2278c2ecf20Sopenharmony_ci			       enum mlxsw_sp_rif_counter_dir dir)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	unsigned int *p_counter_index;
2308c2ecf20Sopenharmony_ci	int err;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
2338c2ecf20Sopenharmony_ci	if (!p_counter_index)
2348c2ecf20Sopenharmony_ci		return -EINVAL;
2358c2ecf20Sopenharmony_ci	err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
2368c2ecf20Sopenharmony_ci				     p_counter_index);
2378c2ecf20Sopenharmony_ci	if (err)
2388c2ecf20Sopenharmony_ci		return err;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
2418c2ecf20Sopenharmony_ci	if (err)
2428c2ecf20Sopenharmony_ci		goto err_counter_clear;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
2458c2ecf20Sopenharmony_ci					*p_counter_index, true, dir);
2468c2ecf20Sopenharmony_ci	if (err)
2478c2ecf20Sopenharmony_ci		goto err_counter_edit;
2488c2ecf20Sopenharmony_ci	mlxsw_sp_rif_counter_valid_set(rif, dir, true);
2498c2ecf20Sopenharmony_ci	return 0;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cierr_counter_edit:
2528c2ecf20Sopenharmony_cierr_counter_clear:
2538c2ecf20Sopenharmony_ci	mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
2548c2ecf20Sopenharmony_ci			      *p_counter_index);
2558c2ecf20Sopenharmony_ci	return err;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_civoid mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
2598c2ecf20Sopenharmony_ci			       struct mlxsw_sp_rif *rif,
2608c2ecf20Sopenharmony_ci			       enum mlxsw_sp_rif_counter_dir dir)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	unsigned int *p_counter_index;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
2658c2ecf20Sopenharmony_ci		return;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
2688c2ecf20Sopenharmony_ci	if (WARN_ON(!p_counter_index))
2698c2ecf20Sopenharmony_ci		return;
2708c2ecf20Sopenharmony_ci	mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
2718c2ecf20Sopenharmony_ci				  *p_counter_index, false, dir);
2728c2ecf20Sopenharmony_ci	mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
2738c2ecf20Sopenharmony_ci			      *p_counter_index);
2748c2ecf20Sopenharmony_ci	mlxsw_sp_rif_counter_valid_set(rif, dir, false);
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
2808c2ecf20Sopenharmony_ci	struct devlink *devlink;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	devlink = priv_to_devlink(mlxsw_sp->core);
2838c2ecf20Sopenharmony_ci	if (!devlink_dpipe_table_counter_enabled(devlink,
2848c2ecf20Sopenharmony_ci						 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))
2858c2ecf20Sopenharmony_ci		return;
2868c2ecf20Sopenharmony_ci	mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistruct mlxsw_sp_prefix_usage {
2998c2ecf20Sopenharmony_ci	DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
3008c2ecf20Sopenharmony_ci};
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
3038c2ecf20Sopenharmony_ci	for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic bool
3068c2ecf20Sopenharmony_cimlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
3078c2ecf20Sopenharmony_ci			 struct mlxsw_sp_prefix_usage *prefix_usage2)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic void
3138c2ecf20Sopenharmony_cimlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
3148c2ecf20Sopenharmony_ci			  struct mlxsw_sp_prefix_usage *prefix_usage2)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic void
3208c2ecf20Sopenharmony_cimlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
3218c2ecf20Sopenharmony_ci			  unsigned char prefix_len)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	set_bit(prefix_len, prefix_usage->b);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic void
3278c2ecf20Sopenharmony_cimlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
3288c2ecf20Sopenharmony_ci			    unsigned char prefix_len)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	clear_bit(prefix_len, prefix_usage->b);
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistruct mlxsw_sp_fib_key {
3348c2ecf20Sopenharmony_ci	unsigned char addr[sizeof(struct in6_addr)];
3358c2ecf20Sopenharmony_ci	unsigned char prefix_len;
3368c2ecf20Sopenharmony_ci};
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cienum mlxsw_sp_fib_entry_type {
3398c2ecf20Sopenharmony_ci	MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
3408c2ecf20Sopenharmony_ci	MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
3418c2ecf20Sopenharmony_ci	MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
3428c2ecf20Sopenharmony_ci	MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE,
3438c2ecf20Sopenharmony_ci	MLXSW_SP_FIB_ENTRY_TYPE_UNREACHABLE,
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/* This is a special case of local delivery, where a packet should be
3468c2ecf20Sopenharmony_ci	 * decapsulated on reception. Note that there is no corresponding ENCAP,
3478c2ecf20Sopenharmony_ci	 * because that's a type of next hop, not of FIB entry. (There can be
3488c2ecf20Sopenharmony_ci	 * several next hops in a REMOTE entry, and some of them may be
3498c2ecf20Sopenharmony_ci	 * encapsulating entries.)
3508c2ecf20Sopenharmony_ci	 */
3518c2ecf20Sopenharmony_ci	MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP,
3528c2ecf20Sopenharmony_ci	MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP,
3538c2ecf20Sopenharmony_ci};
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistruct mlxsw_sp_nexthop_group;
3568c2ecf20Sopenharmony_cistruct mlxsw_sp_fib_entry;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistruct mlxsw_sp_fib_node {
3598c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_entry *fib_entry;
3608c2ecf20Sopenharmony_ci	struct list_head list;
3618c2ecf20Sopenharmony_ci	struct rhash_head ht_node;
3628c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib;
3638c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_key key;
3648c2ecf20Sopenharmony_ci};
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistruct mlxsw_sp_fib_entry_decap {
3678c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
3688c2ecf20Sopenharmony_ci	u32 tunnel_index;
3698c2ecf20Sopenharmony_ci};
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistruct mlxsw_sp_fib_entry {
3728c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node;
3738c2ecf20Sopenharmony_ci	enum mlxsw_sp_fib_entry_type type;
3748c2ecf20Sopenharmony_ci	struct list_head nexthop_group_node;
3758c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group *nh_group;
3768c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_entry_decap decap; /* Valid for decap entries. */
3778c2ecf20Sopenharmony_ci};
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistruct mlxsw_sp_fib4_entry {
3808c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_entry common;
3818c2ecf20Sopenharmony_ci	u32 tb_id;
3828c2ecf20Sopenharmony_ci	u32 prio;
3838c2ecf20Sopenharmony_ci	u8 tos;
3848c2ecf20Sopenharmony_ci	u8 type;
3858c2ecf20Sopenharmony_ci};
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistruct mlxsw_sp_fib6_entry {
3888c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_entry common;
3898c2ecf20Sopenharmony_ci	struct list_head rt6_list;
3908c2ecf20Sopenharmony_ci	unsigned int nrt6;
3918c2ecf20Sopenharmony_ci};
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistruct mlxsw_sp_rt6 {
3948c2ecf20Sopenharmony_ci	struct list_head list;
3958c2ecf20Sopenharmony_ci	struct fib6_info *rt;
3968c2ecf20Sopenharmony_ci};
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistruct mlxsw_sp_lpm_tree {
3998c2ecf20Sopenharmony_ci	u8 id; /* tree ID */
4008c2ecf20Sopenharmony_ci	unsigned int ref_count;
4018c2ecf20Sopenharmony_ci	enum mlxsw_sp_l3proto proto;
4028c2ecf20Sopenharmony_ci	unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
4038c2ecf20Sopenharmony_ci	struct mlxsw_sp_prefix_usage prefix_usage;
4048c2ecf20Sopenharmony_ci};
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistruct mlxsw_sp_fib {
4078c2ecf20Sopenharmony_ci	struct rhashtable ht;
4088c2ecf20Sopenharmony_ci	struct list_head node_list;
4098c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
4108c2ecf20Sopenharmony_ci	struct mlxsw_sp_lpm_tree *lpm_tree;
4118c2ecf20Sopenharmony_ci	enum mlxsw_sp_l3proto proto;
4128c2ecf20Sopenharmony_ci};
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistruct mlxsw_sp_vr {
4158c2ecf20Sopenharmony_ci	u16 id; /* virtual router ID */
4168c2ecf20Sopenharmony_ci	u32 tb_id; /* kernel fib table id */
4178c2ecf20Sopenharmony_ci	unsigned int rif_count;
4188c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib4;
4198c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib6;
4208c2ecf20Sopenharmony_ci	struct mlxsw_sp_mr_table *mr_table[MLXSW_SP_L3_PROTO_MAX];
4218c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *ul_rif;
4228c2ecf20Sopenharmony_ci	refcount_t ul_rif_refcnt;
4238c2ecf20Sopenharmony_ci};
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp_fib_ht_params;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp *mlxsw_sp,
4288c2ecf20Sopenharmony_ci						struct mlxsw_sp_vr *vr,
4298c2ecf20Sopenharmony_ci						enum mlxsw_sp_l3proto proto)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	struct mlxsw_sp_lpm_tree *lpm_tree;
4328c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib;
4338c2ecf20Sopenharmony_ci	int err;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	lpm_tree = mlxsw_sp->router->lpm.proto_trees[proto];
4368c2ecf20Sopenharmony_ci	fib = kzalloc(sizeof(*fib), GFP_KERNEL);
4378c2ecf20Sopenharmony_ci	if (!fib)
4388c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
4398c2ecf20Sopenharmony_ci	err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
4408c2ecf20Sopenharmony_ci	if (err)
4418c2ecf20Sopenharmony_ci		goto err_rhashtable_init;
4428c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fib->node_list);
4438c2ecf20Sopenharmony_ci	fib->proto = proto;
4448c2ecf20Sopenharmony_ci	fib->vr = vr;
4458c2ecf20Sopenharmony_ci	fib->lpm_tree = lpm_tree;
4468c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_tree_hold(lpm_tree);
4478c2ecf20Sopenharmony_ci	err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, lpm_tree->id);
4488c2ecf20Sopenharmony_ci	if (err)
4498c2ecf20Sopenharmony_ci		goto err_lpm_tree_bind;
4508c2ecf20Sopenharmony_ci	return fib;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cierr_lpm_tree_bind:
4538c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
4548c2ecf20Sopenharmony_cierr_rhashtable_init:
4558c2ecf20Sopenharmony_ci	kfree(fib);
4568c2ecf20Sopenharmony_ci	return ERR_PTR(err);
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic void mlxsw_sp_fib_destroy(struct mlxsw_sp *mlxsw_sp,
4608c2ecf20Sopenharmony_ci				 struct mlxsw_sp_fib *fib)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
4638c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
4648c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&fib->node_list));
4658c2ecf20Sopenharmony_ci	rhashtable_destroy(&fib->ht);
4668c2ecf20Sopenharmony_ci	kfree(fib);
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic struct mlxsw_sp_lpm_tree *
4708c2ecf20Sopenharmony_cimlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	static struct mlxsw_sp_lpm_tree *lpm_tree;
4738c2ecf20Sopenharmony_ci	int i;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
4768c2ecf20Sopenharmony_ci		lpm_tree = &mlxsw_sp->router->lpm.trees[i];
4778c2ecf20Sopenharmony_ci		if (lpm_tree->ref_count == 0)
4788c2ecf20Sopenharmony_ci			return lpm_tree;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci	return NULL;
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
4848c2ecf20Sopenharmony_ci				   struct mlxsw_sp_lpm_tree *lpm_tree)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	char ralta_pl[MLXSW_REG_RALTA_LEN];
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	mlxsw_reg_ralta_pack(ralta_pl, true,
4898c2ecf20Sopenharmony_ci			     (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
4908c2ecf20Sopenharmony_ci			     lpm_tree->id);
4918c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cistatic void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
4958c2ecf20Sopenharmony_ci				   struct mlxsw_sp_lpm_tree *lpm_tree)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	char ralta_pl[MLXSW_REG_RALTA_LEN];
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	mlxsw_reg_ralta_pack(ralta_pl, false,
5008c2ecf20Sopenharmony_ci			     (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
5018c2ecf20Sopenharmony_ci			     lpm_tree->id);
5028c2ecf20Sopenharmony_ci	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic int
5068c2ecf20Sopenharmony_cimlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
5078c2ecf20Sopenharmony_ci				  struct mlxsw_sp_prefix_usage *prefix_usage,
5088c2ecf20Sopenharmony_ci				  struct mlxsw_sp_lpm_tree *lpm_tree)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	char ralst_pl[MLXSW_REG_RALST_LEN];
5118c2ecf20Sopenharmony_ci	u8 root_bin = 0;
5128c2ecf20Sopenharmony_ci	u8 prefix;
5138c2ecf20Sopenharmony_ci	u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
5168c2ecf20Sopenharmony_ci		root_bin = prefix;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
5198c2ecf20Sopenharmony_ci	mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
5208c2ecf20Sopenharmony_ci		if (prefix == 0)
5218c2ecf20Sopenharmony_ci			continue;
5228c2ecf20Sopenharmony_ci		mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
5238c2ecf20Sopenharmony_ci					 MLXSW_REG_RALST_BIN_NO_CHILD);
5248c2ecf20Sopenharmony_ci		last_prefix = prefix;
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic struct mlxsw_sp_lpm_tree *
5308c2ecf20Sopenharmony_cimlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
5318c2ecf20Sopenharmony_ci			 struct mlxsw_sp_prefix_usage *prefix_usage,
5328c2ecf20Sopenharmony_ci			 enum mlxsw_sp_l3proto proto)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	struct mlxsw_sp_lpm_tree *lpm_tree;
5358c2ecf20Sopenharmony_ci	int err;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
5388c2ecf20Sopenharmony_ci	if (!lpm_tree)
5398c2ecf20Sopenharmony_ci		return ERR_PTR(-EBUSY);
5408c2ecf20Sopenharmony_ci	lpm_tree->proto = proto;
5418c2ecf20Sopenharmony_ci	err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
5428c2ecf20Sopenharmony_ci	if (err)
5438c2ecf20Sopenharmony_ci		return ERR_PTR(err);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
5468c2ecf20Sopenharmony_ci						lpm_tree);
5478c2ecf20Sopenharmony_ci	if (err)
5488c2ecf20Sopenharmony_ci		goto err_left_struct_set;
5498c2ecf20Sopenharmony_ci	memcpy(&lpm_tree->prefix_usage, prefix_usage,
5508c2ecf20Sopenharmony_ci	       sizeof(lpm_tree->prefix_usage));
5518c2ecf20Sopenharmony_ci	memset(&lpm_tree->prefix_ref_count, 0,
5528c2ecf20Sopenharmony_ci	       sizeof(lpm_tree->prefix_ref_count));
5538c2ecf20Sopenharmony_ci	lpm_tree->ref_count = 1;
5548c2ecf20Sopenharmony_ci	return lpm_tree;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cierr_left_struct_set:
5578c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
5588c2ecf20Sopenharmony_ci	return ERR_PTR(err);
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
5628c2ecf20Sopenharmony_ci				      struct mlxsw_sp_lpm_tree *lpm_tree)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_cistatic struct mlxsw_sp_lpm_tree *
5688c2ecf20Sopenharmony_cimlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
5698c2ecf20Sopenharmony_ci		      struct mlxsw_sp_prefix_usage *prefix_usage,
5708c2ecf20Sopenharmony_ci		      enum mlxsw_sp_l3proto proto)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	struct mlxsw_sp_lpm_tree *lpm_tree;
5738c2ecf20Sopenharmony_ci	int i;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
5768c2ecf20Sopenharmony_ci		lpm_tree = &mlxsw_sp->router->lpm.trees[i];
5778c2ecf20Sopenharmony_ci		if (lpm_tree->ref_count != 0 &&
5788c2ecf20Sopenharmony_ci		    lpm_tree->proto == proto &&
5798c2ecf20Sopenharmony_ci		    mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
5808c2ecf20Sopenharmony_ci					     prefix_usage)) {
5818c2ecf20Sopenharmony_ci			mlxsw_sp_lpm_tree_hold(lpm_tree);
5828c2ecf20Sopenharmony_ci			return lpm_tree;
5838c2ecf20Sopenharmony_ci		}
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci	return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto);
5868c2ecf20Sopenharmony_ci}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cistatic void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	lpm_tree->ref_count++;
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
5948c2ecf20Sopenharmony_ci				  struct mlxsw_sp_lpm_tree *lpm_tree)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	if (--lpm_tree->ref_count == 0)
5978c2ecf20Sopenharmony_ci		mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
6058c2ecf20Sopenharmony_ci	struct mlxsw_sp_lpm_tree *lpm_tree;
6068c2ecf20Sopenharmony_ci	u64 max_trees;
6078c2ecf20Sopenharmony_ci	int err, i;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
6108c2ecf20Sopenharmony_ci		return -EIO;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
6138c2ecf20Sopenharmony_ci	mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
6148c2ecf20Sopenharmony_ci	mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,
6158c2ecf20Sopenharmony_ci					     sizeof(struct mlxsw_sp_lpm_tree),
6168c2ecf20Sopenharmony_ci					     GFP_KERNEL);
6178c2ecf20Sopenharmony_ci	if (!mlxsw_sp->router->lpm.trees)
6188c2ecf20Sopenharmony_ci		return -ENOMEM;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
6218c2ecf20Sopenharmony_ci		lpm_tree = &mlxsw_sp->router->lpm.trees[i];
6228c2ecf20Sopenharmony_ci		lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
6268c2ecf20Sopenharmony_ci					 MLXSW_SP_L3_PROTO_IPV4);
6278c2ecf20Sopenharmony_ci	if (IS_ERR(lpm_tree)) {
6288c2ecf20Sopenharmony_ci		err = PTR_ERR(lpm_tree);
6298c2ecf20Sopenharmony_ci		goto err_ipv4_tree_get;
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci	mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV4] = lpm_tree;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
6348c2ecf20Sopenharmony_ci					 MLXSW_SP_L3_PROTO_IPV6);
6358c2ecf20Sopenharmony_ci	if (IS_ERR(lpm_tree)) {
6368c2ecf20Sopenharmony_ci		err = PTR_ERR(lpm_tree);
6378c2ecf20Sopenharmony_ci		goto err_ipv6_tree_get;
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci	mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV6] = lpm_tree;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	return 0;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cierr_ipv6_tree_get:
6448c2ecf20Sopenharmony_ci	lpm_tree = mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV4];
6458c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
6468c2ecf20Sopenharmony_cierr_ipv4_tree_get:
6478c2ecf20Sopenharmony_ci	kfree(mlxsw_sp->router->lpm.trees);
6488c2ecf20Sopenharmony_ci	return err;
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cistatic void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	struct mlxsw_sp_lpm_tree *lpm_tree;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	lpm_tree = mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV6];
6568c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	lpm_tree = mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV4];
6598c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	kfree(mlxsw_sp->router->lpm.trees);
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	return !!vr->fib4 || !!vr->fib6 ||
6678c2ecf20Sopenharmony_ci	       !!vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] ||
6688c2ecf20Sopenharmony_ci	       !!vr->mr_table[MLXSW_SP_L3_PROTO_IPV6];
6698c2ecf20Sopenharmony_ci}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_cistatic struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
6728c2ecf20Sopenharmony_ci{
6738c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
6748c2ecf20Sopenharmony_ci	int i;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
6778c2ecf20Sopenharmony_ci		vr = &mlxsw_sp->router->vrs[i];
6788c2ecf20Sopenharmony_ci		if (!mlxsw_sp_vr_is_used(vr))
6798c2ecf20Sopenharmony_ci			return vr;
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci	return NULL;
6828c2ecf20Sopenharmony_ci}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_cistatic int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
6858c2ecf20Sopenharmony_ci				     const struct mlxsw_sp_fib *fib, u8 tree_id)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	char raltb_pl[MLXSW_REG_RALTB_LEN];
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
6908c2ecf20Sopenharmony_ci			     (enum mlxsw_reg_ralxx_protocol) fib->proto,
6918c2ecf20Sopenharmony_ci			     tree_id);
6928c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
6968c2ecf20Sopenharmony_ci				       const struct mlxsw_sp_fib *fib)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	char raltb_pl[MLXSW_REG_RALTB_LEN];
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	/* Bind to tree 0 which is default */
7018c2ecf20Sopenharmony_ci	mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
7028c2ecf20Sopenharmony_ci			     (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
7038c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cistatic u32 mlxsw_sp_fix_tb_id(u32 tb_id)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	/* For our purpose, squash main, default and local tables into one */
7098c2ecf20Sopenharmony_ci	if (tb_id == RT_TABLE_LOCAL || tb_id == RT_TABLE_DEFAULT)
7108c2ecf20Sopenharmony_ci		tb_id = RT_TABLE_MAIN;
7118c2ecf20Sopenharmony_ci	return tb_id;
7128c2ecf20Sopenharmony_ci}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_cistatic struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
7158c2ecf20Sopenharmony_ci					    u32 tb_id)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
7188c2ecf20Sopenharmony_ci	int i;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	tb_id = mlxsw_sp_fix_tb_id(tb_id);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
7238c2ecf20Sopenharmony_ci		vr = &mlxsw_sp->router->vrs[i];
7248c2ecf20Sopenharmony_ci		if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
7258c2ecf20Sopenharmony_ci			return vr;
7268c2ecf20Sopenharmony_ci	}
7278c2ecf20Sopenharmony_ci	return NULL;
7288c2ecf20Sopenharmony_ci}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ciint mlxsw_sp_router_tb_id_vr_id(struct mlxsw_sp *mlxsw_sp, u32 tb_id,
7318c2ecf20Sopenharmony_ci				u16 *vr_id)
7328c2ecf20Sopenharmony_ci{
7338c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
7348c2ecf20Sopenharmony_ci	int err = 0;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
7378c2ecf20Sopenharmony_ci	vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
7388c2ecf20Sopenharmony_ci	if (!vr) {
7398c2ecf20Sopenharmony_ci		err = -ESRCH;
7408c2ecf20Sopenharmony_ci		goto out;
7418c2ecf20Sopenharmony_ci	}
7428c2ecf20Sopenharmony_ci	*vr_id = vr->id;
7438c2ecf20Sopenharmony_ciout:
7448c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
7458c2ecf20Sopenharmony_ci	return err;
7468c2ecf20Sopenharmony_ci}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
7498c2ecf20Sopenharmony_ci					    enum mlxsw_sp_l3proto proto)
7508c2ecf20Sopenharmony_ci{
7518c2ecf20Sopenharmony_ci	switch (proto) {
7528c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
7538c2ecf20Sopenharmony_ci		return vr->fib4;
7548c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
7558c2ecf20Sopenharmony_ci		return vr->fib6;
7568c2ecf20Sopenharmony_ci	}
7578c2ecf20Sopenharmony_ci	return NULL;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
7618c2ecf20Sopenharmony_ci					      u32 tb_id,
7628c2ecf20Sopenharmony_ci					      struct netlink_ext_ack *extack)
7638c2ecf20Sopenharmony_ci{
7648c2ecf20Sopenharmony_ci	struct mlxsw_sp_mr_table *mr4_table, *mr6_table;
7658c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib4;
7668c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib6;
7678c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
7688c2ecf20Sopenharmony_ci	int err;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
7718c2ecf20Sopenharmony_ci	if (!vr) {
7728c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported virtual routers");
7738c2ecf20Sopenharmony_ci		return ERR_PTR(-EBUSY);
7748c2ecf20Sopenharmony_ci	}
7758c2ecf20Sopenharmony_ci	fib4 = mlxsw_sp_fib_create(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
7768c2ecf20Sopenharmony_ci	if (IS_ERR(fib4))
7778c2ecf20Sopenharmony_ci		return ERR_CAST(fib4);
7788c2ecf20Sopenharmony_ci	fib6 = mlxsw_sp_fib_create(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
7798c2ecf20Sopenharmony_ci	if (IS_ERR(fib6)) {
7808c2ecf20Sopenharmony_ci		err = PTR_ERR(fib6);
7818c2ecf20Sopenharmony_ci		goto err_fib6_create;
7828c2ecf20Sopenharmony_ci	}
7838c2ecf20Sopenharmony_ci	mr4_table = mlxsw_sp_mr_table_create(mlxsw_sp, vr->id,
7848c2ecf20Sopenharmony_ci					     MLXSW_SP_L3_PROTO_IPV4);
7858c2ecf20Sopenharmony_ci	if (IS_ERR(mr4_table)) {
7868c2ecf20Sopenharmony_ci		err = PTR_ERR(mr4_table);
7878c2ecf20Sopenharmony_ci		goto err_mr4_table_create;
7888c2ecf20Sopenharmony_ci	}
7898c2ecf20Sopenharmony_ci	mr6_table = mlxsw_sp_mr_table_create(mlxsw_sp, vr->id,
7908c2ecf20Sopenharmony_ci					     MLXSW_SP_L3_PROTO_IPV6);
7918c2ecf20Sopenharmony_ci	if (IS_ERR(mr6_table)) {
7928c2ecf20Sopenharmony_ci		err = PTR_ERR(mr6_table);
7938c2ecf20Sopenharmony_ci		goto err_mr6_table_create;
7948c2ecf20Sopenharmony_ci	}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	vr->fib4 = fib4;
7978c2ecf20Sopenharmony_ci	vr->fib6 = fib6;
7988c2ecf20Sopenharmony_ci	vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] = mr4_table;
7998c2ecf20Sopenharmony_ci	vr->mr_table[MLXSW_SP_L3_PROTO_IPV6] = mr6_table;
8008c2ecf20Sopenharmony_ci	vr->tb_id = tb_id;
8018c2ecf20Sopenharmony_ci	return vr;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_cierr_mr6_table_create:
8048c2ecf20Sopenharmony_ci	mlxsw_sp_mr_table_destroy(mr4_table);
8058c2ecf20Sopenharmony_cierr_mr4_table_create:
8068c2ecf20Sopenharmony_ci	mlxsw_sp_fib_destroy(mlxsw_sp, fib6);
8078c2ecf20Sopenharmony_cierr_fib6_create:
8088c2ecf20Sopenharmony_ci	mlxsw_sp_fib_destroy(mlxsw_sp, fib4);
8098c2ecf20Sopenharmony_ci	return ERR_PTR(err);
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_cistatic void mlxsw_sp_vr_destroy(struct mlxsw_sp *mlxsw_sp,
8138c2ecf20Sopenharmony_ci				struct mlxsw_sp_vr *vr)
8148c2ecf20Sopenharmony_ci{
8158c2ecf20Sopenharmony_ci	mlxsw_sp_mr_table_destroy(vr->mr_table[MLXSW_SP_L3_PROTO_IPV6]);
8168c2ecf20Sopenharmony_ci	vr->mr_table[MLXSW_SP_L3_PROTO_IPV6] = NULL;
8178c2ecf20Sopenharmony_ci	mlxsw_sp_mr_table_destroy(vr->mr_table[MLXSW_SP_L3_PROTO_IPV4]);
8188c2ecf20Sopenharmony_ci	vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] = NULL;
8198c2ecf20Sopenharmony_ci	mlxsw_sp_fib_destroy(mlxsw_sp, vr->fib6);
8208c2ecf20Sopenharmony_ci	vr->fib6 = NULL;
8218c2ecf20Sopenharmony_ci	mlxsw_sp_fib_destroy(mlxsw_sp, vr->fib4);
8228c2ecf20Sopenharmony_ci	vr->fib4 = NULL;
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_cistatic struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id,
8268c2ecf20Sopenharmony_ci					   struct netlink_ext_ack *extack)
8278c2ecf20Sopenharmony_ci{
8288c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	tb_id = mlxsw_sp_fix_tb_id(tb_id);
8318c2ecf20Sopenharmony_ci	vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
8328c2ecf20Sopenharmony_ci	if (!vr)
8338c2ecf20Sopenharmony_ci		vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id, extack);
8348c2ecf20Sopenharmony_ci	return vr;
8358c2ecf20Sopenharmony_ci}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_cistatic void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr)
8388c2ecf20Sopenharmony_ci{
8398c2ecf20Sopenharmony_ci	if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
8408c2ecf20Sopenharmony_ci	    list_empty(&vr->fib6->node_list) &&
8418c2ecf20Sopenharmony_ci	    mlxsw_sp_mr_table_empty(vr->mr_table[MLXSW_SP_L3_PROTO_IPV4]) &&
8428c2ecf20Sopenharmony_ci	    mlxsw_sp_mr_table_empty(vr->mr_table[MLXSW_SP_L3_PROTO_IPV6]))
8438c2ecf20Sopenharmony_ci		mlxsw_sp_vr_destroy(mlxsw_sp, vr);
8448c2ecf20Sopenharmony_ci}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_cistatic bool
8478c2ecf20Sopenharmony_cimlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr,
8488c2ecf20Sopenharmony_ci				    enum mlxsw_sp_l3proto proto, u8 tree_id)
8498c2ecf20Sopenharmony_ci{
8508c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	if (!mlxsw_sp_vr_is_used(vr))
8538c2ecf20Sopenharmony_ci		return false;
8548c2ecf20Sopenharmony_ci	if (fib->lpm_tree->id == tree_id)
8558c2ecf20Sopenharmony_ci		return true;
8568c2ecf20Sopenharmony_ci	return false;
8578c2ecf20Sopenharmony_ci}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
8608c2ecf20Sopenharmony_ci					struct mlxsw_sp_fib *fib,
8618c2ecf20Sopenharmony_ci					struct mlxsw_sp_lpm_tree *new_tree)
8628c2ecf20Sopenharmony_ci{
8638c2ecf20Sopenharmony_ci	struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
8648c2ecf20Sopenharmony_ci	int err;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	fib->lpm_tree = new_tree;
8678c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_tree_hold(new_tree);
8688c2ecf20Sopenharmony_ci	err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
8698c2ecf20Sopenharmony_ci	if (err)
8708c2ecf20Sopenharmony_ci		goto err_tree_bind;
8718c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree);
8728c2ecf20Sopenharmony_ci	return 0;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_cierr_tree_bind:
8758c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_tree_put(mlxsw_sp, new_tree);
8768c2ecf20Sopenharmony_ci	fib->lpm_tree = old_tree;
8778c2ecf20Sopenharmony_ci	return err;
8788c2ecf20Sopenharmony_ci}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_cistatic int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
8818c2ecf20Sopenharmony_ci					 struct mlxsw_sp_fib *fib,
8828c2ecf20Sopenharmony_ci					 struct mlxsw_sp_lpm_tree *new_tree)
8838c2ecf20Sopenharmony_ci{
8848c2ecf20Sopenharmony_ci	enum mlxsw_sp_l3proto proto = fib->proto;
8858c2ecf20Sopenharmony_ci	struct mlxsw_sp_lpm_tree *old_tree;
8868c2ecf20Sopenharmony_ci	u8 old_id, new_id = new_tree->id;
8878c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
8888c2ecf20Sopenharmony_ci	int i, err;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	old_tree = mlxsw_sp->router->lpm.proto_trees[proto];
8918c2ecf20Sopenharmony_ci	old_id = old_tree->id;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
8948c2ecf20Sopenharmony_ci		vr = &mlxsw_sp->router->vrs[i];
8958c2ecf20Sopenharmony_ci		if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id))
8968c2ecf20Sopenharmony_ci			continue;
8978c2ecf20Sopenharmony_ci		err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
8988c2ecf20Sopenharmony_ci						   mlxsw_sp_vr_fib(vr, proto),
8998c2ecf20Sopenharmony_ci						   new_tree);
9008c2ecf20Sopenharmony_ci		if (err)
9018c2ecf20Sopenharmony_ci			goto err_tree_replace;
9028c2ecf20Sopenharmony_ci	}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	memcpy(new_tree->prefix_ref_count, old_tree->prefix_ref_count,
9058c2ecf20Sopenharmony_ci	       sizeof(new_tree->prefix_ref_count));
9068c2ecf20Sopenharmony_ci	mlxsw_sp->router->lpm.proto_trees[proto] = new_tree;
9078c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree);
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	return 0;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cierr_tree_replace:
9128c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--) {
9138c2ecf20Sopenharmony_ci		if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id))
9148c2ecf20Sopenharmony_ci			continue;
9158c2ecf20Sopenharmony_ci		mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
9168c2ecf20Sopenharmony_ci					     mlxsw_sp_vr_fib(vr, proto),
9178c2ecf20Sopenharmony_ci					     old_tree);
9188c2ecf20Sopenharmony_ci	}
9198c2ecf20Sopenharmony_ci	return err;
9208c2ecf20Sopenharmony_ci}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_cistatic int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
9238c2ecf20Sopenharmony_ci{
9248c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
9258c2ecf20Sopenharmony_ci	u64 max_vrs;
9268c2ecf20Sopenharmony_ci	int i;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
9298c2ecf20Sopenharmony_ci		return -EIO;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
9328c2ecf20Sopenharmony_ci	mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
9338c2ecf20Sopenharmony_ci					GFP_KERNEL);
9348c2ecf20Sopenharmony_ci	if (!mlxsw_sp->router->vrs)
9358c2ecf20Sopenharmony_ci		return -ENOMEM;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	for (i = 0; i < max_vrs; i++) {
9388c2ecf20Sopenharmony_ci		vr = &mlxsw_sp->router->vrs[i];
9398c2ecf20Sopenharmony_ci		vr->id = i;
9408c2ecf20Sopenharmony_ci	}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	return 0;
9438c2ecf20Sopenharmony_ci}
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_cistatic void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
9488c2ecf20Sopenharmony_ci{
9498c2ecf20Sopenharmony_ci	/* At this stage we're guaranteed not to have new incoming
9508c2ecf20Sopenharmony_ci	 * FIB notifications and the work queue is free from FIBs
9518c2ecf20Sopenharmony_ci	 * sitting on top of mlxsw netdevs. However, we can still
9528c2ecf20Sopenharmony_ci	 * have other FIBs queued. Flush the queue before flushing
9538c2ecf20Sopenharmony_ci	 * the device's tables. No need for locks, as we're the only
9548c2ecf20Sopenharmony_ci	 * writer.
9558c2ecf20Sopenharmony_ci	 */
9568c2ecf20Sopenharmony_ci	mlxsw_core_flush_owq();
9578c2ecf20Sopenharmony_ci	mlxsw_sp_router_fib_flush(mlxsw_sp);
9588c2ecf20Sopenharmony_ci	kfree(mlxsw_sp->router->vrs);
9598c2ecf20Sopenharmony_ci}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_cistatic struct net_device *
9628c2ecf20Sopenharmony_ci__mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	struct ip_tunnel *tun = netdev_priv(ol_dev);
9658c2ecf20Sopenharmony_ci	struct net *net = dev_net(ol_dev);
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	return dev_get_by_index_rcu(net, tun->parms.link);
9688c2ecf20Sopenharmony_ci}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ciu32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev)
9718c2ecf20Sopenharmony_ci{
9728c2ecf20Sopenharmony_ci	struct net_device *d;
9738c2ecf20Sopenharmony_ci	u32 tb_id;
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	rcu_read_lock();
9768c2ecf20Sopenharmony_ci	d = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);
9778c2ecf20Sopenharmony_ci	if (d)
9788c2ecf20Sopenharmony_ci		tb_id = l3mdev_fib_table(d) ? : RT_TABLE_MAIN;
9798c2ecf20Sopenharmony_ci	else
9808c2ecf20Sopenharmony_ci		tb_id = RT_TABLE_MAIN;
9818c2ecf20Sopenharmony_ci	rcu_read_unlock();
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	return tb_id;
9848c2ecf20Sopenharmony_ci}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_cistatic struct mlxsw_sp_rif *
9878c2ecf20Sopenharmony_cimlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
9888c2ecf20Sopenharmony_ci		    const struct mlxsw_sp_rif_params *params,
9898c2ecf20Sopenharmony_ci		    struct netlink_ext_ack *extack);
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_cistatic struct mlxsw_sp_rif_ipip_lb *
9928c2ecf20Sopenharmony_cimlxsw_sp_ipip_ol_ipip_lb_create(struct mlxsw_sp *mlxsw_sp,
9938c2ecf20Sopenharmony_ci				enum mlxsw_sp_ipip_type ipipt,
9948c2ecf20Sopenharmony_ci				struct net_device *ol_dev,
9958c2ecf20Sopenharmony_ci				struct netlink_ext_ack *extack)
9968c2ecf20Sopenharmony_ci{
9978c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_params_ipip_lb lb_params;
9988c2ecf20Sopenharmony_ci	const struct mlxsw_sp_ipip_ops *ipip_ops;
9998c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt];
10028c2ecf20Sopenharmony_ci	lb_params = (struct mlxsw_sp_rif_params_ipip_lb) {
10038c2ecf20Sopenharmony_ci		.common.dev = ol_dev,
10048c2ecf20Sopenharmony_ci		.common.lag = false,
10058c2ecf20Sopenharmony_ci		.lb_config = ipip_ops->ol_loopback_config(mlxsw_sp, ol_dev),
10068c2ecf20Sopenharmony_ci	};
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_create(mlxsw_sp, &lb_params.common, extack);
10098c2ecf20Sopenharmony_ci	if (IS_ERR(rif))
10108c2ecf20Sopenharmony_ci		return ERR_CAST(rif);
10118c2ecf20Sopenharmony_ci	return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
10128c2ecf20Sopenharmony_ci}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_cistatic struct mlxsw_sp_ipip_entry *
10158c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_alloc(struct mlxsw_sp *mlxsw_sp,
10168c2ecf20Sopenharmony_ci			  enum mlxsw_sp_ipip_type ipipt,
10178c2ecf20Sopenharmony_ci			  struct net_device *ol_dev)
10188c2ecf20Sopenharmony_ci{
10198c2ecf20Sopenharmony_ci	const struct mlxsw_sp_ipip_ops *ipip_ops;
10208c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
10218c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ret = NULL;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt];
10248c2ecf20Sopenharmony_ci	ipip_entry = kzalloc(sizeof(*ipip_entry), GFP_KERNEL);
10258c2ecf20Sopenharmony_ci	if (!ipip_entry)
10268c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	ipip_entry->ol_lb = mlxsw_sp_ipip_ol_ipip_lb_create(mlxsw_sp, ipipt,
10298c2ecf20Sopenharmony_ci							    ol_dev, NULL);
10308c2ecf20Sopenharmony_ci	if (IS_ERR(ipip_entry->ol_lb)) {
10318c2ecf20Sopenharmony_ci		ret = ERR_CAST(ipip_entry->ol_lb);
10328c2ecf20Sopenharmony_ci		goto err_ol_ipip_lb_create;
10338c2ecf20Sopenharmony_ci	}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	ipip_entry->ipipt = ipipt;
10368c2ecf20Sopenharmony_ci	ipip_entry->ol_dev = ol_dev;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	switch (ipip_ops->ul_proto) {
10398c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
10408c2ecf20Sopenharmony_ci		ipip_entry->parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev);
10418c2ecf20Sopenharmony_ci		break;
10428c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
10438c2ecf20Sopenharmony_ci		WARN_ON(1);
10448c2ecf20Sopenharmony_ci		break;
10458c2ecf20Sopenharmony_ci	}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	return ipip_entry;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_cierr_ol_ipip_lb_create:
10508c2ecf20Sopenharmony_ci	kfree(ipip_entry);
10518c2ecf20Sopenharmony_ci	return ret;
10528c2ecf20Sopenharmony_ci}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_cistatic void
10558c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_dealloc(struct mlxsw_sp_ipip_entry *ipip_entry)
10568c2ecf20Sopenharmony_ci{
10578c2ecf20Sopenharmony_ci	mlxsw_sp_rif_destroy(&ipip_entry->ol_lb->common);
10588c2ecf20Sopenharmony_ci	kfree(ipip_entry);
10598c2ecf20Sopenharmony_ci}
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_cistatic bool
10628c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_saddr_matches(struct mlxsw_sp *mlxsw_sp,
10638c2ecf20Sopenharmony_ci				  const enum mlxsw_sp_l3proto ul_proto,
10648c2ecf20Sopenharmony_ci				  union mlxsw_sp_l3addr saddr,
10658c2ecf20Sopenharmony_ci				  u32 ul_tb_id,
10668c2ecf20Sopenharmony_ci				  struct mlxsw_sp_ipip_entry *ipip_entry)
10678c2ecf20Sopenharmony_ci{
10688c2ecf20Sopenharmony_ci	u32 tun_ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
10698c2ecf20Sopenharmony_ci	enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
10708c2ecf20Sopenharmony_ci	union mlxsw_sp_l3addr tun_saddr;
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
10738c2ecf20Sopenharmony_ci		return false;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	tun_saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ipip_entry->ol_dev);
10768c2ecf20Sopenharmony_ci	return tun_ul_tb_id == ul_tb_id &&
10778c2ecf20Sopenharmony_ci	       mlxsw_sp_l3addr_eq(&tun_saddr, &saddr);
10788c2ecf20Sopenharmony_ci}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_cistatic int
10818c2ecf20Sopenharmony_cimlxsw_sp_fib_entry_decap_init(struct mlxsw_sp *mlxsw_sp,
10828c2ecf20Sopenharmony_ci			      struct mlxsw_sp_fib_entry *fib_entry,
10838c2ecf20Sopenharmony_ci			      struct mlxsw_sp_ipip_entry *ipip_entry)
10848c2ecf20Sopenharmony_ci{
10858c2ecf20Sopenharmony_ci	u32 tunnel_index;
10868c2ecf20Sopenharmony_ci	int err;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,
10898c2ecf20Sopenharmony_ci				  1, &tunnel_index);
10908c2ecf20Sopenharmony_ci	if (err)
10918c2ecf20Sopenharmony_ci		return err;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	ipip_entry->decap_fib_entry = fib_entry;
10948c2ecf20Sopenharmony_ci	fib_entry->decap.ipip_entry = ipip_entry;
10958c2ecf20Sopenharmony_ci	fib_entry->decap.tunnel_index = tunnel_index;
10968c2ecf20Sopenharmony_ci	return 0;
10978c2ecf20Sopenharmony_ci}
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_cistatic void mlxsw_sp_fib_entry_decap_fini(struct mlxsw_sp *mlxsw_sp,
11008c2ecf20Sopenharmony_ci					  struct mlxsw_sp_fib_entry *fib_entry)
11018c2ecf20Sopenharmony_ci{
11028c2ecf20Sopenharmony_ci	/* Unlink this node from the IPIP entry that it's the decap entry of. */
11038c2ecf20Sopenharmony_ci	fib_entry->decap.ipip_entry->decap_fib_entry = NULL;
11048c2ecf20Sopenharmony_ci	fib_entry->decap.ipip_entry = NULL;
11058c2ecf20Sopenharmony_ci	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,
11068c2ecf20Sopenharmony_ci			   1, fib_entry->decap.tunnel_index);
11078c2ecf20Sopenharmony_ci}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fib_node *
11108c2ecf20Sopenharmony_cimlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
11118c2ecf20Sopenharmony_ci			 size_t addr_len, unsigned char prefix_len);
11128c2ecf20Sopenharmony_cistatic int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
11138c2ecf20Sopenharmony_ci				     struct mlxsw_sp_fib_entry *fib_entry);
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_cistatic void
11168c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_demote_decap(struct mlxsw_sp *mlxsw_sp,
11178c2ecf20Sopenharmony_ci				 struct mlxsw_sp_ipip_entry *ipip_entry)
11188c2ecf20Sopenharmony_ci{
11198c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_entry *fib_entry = ipip_entry->decap_fib_entry;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, fib_entry);
11228c2ecf20Sopenharmony_ci	fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
11258c2ecf20Sopenharmony_ci}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_cistatic void
11288c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_promote_decap(struct mlxsw_sp *mlxsw_sp,
11298c2ecf20Sopenharmony_ci				  struct mlxsw_sp_ipip_entry *ipip_entry,
11308c2ecf20Sopenharmony_ci				  struct mlxsw_sp_fib_entry *decap_fib_entry)
11318c2ecf20Sopenharmony_ci{
11328c2ecf20Sopenharmony_ci	if (mlxsw_sp_fib_entry_decap_init(mlxsw_sp, decap_fib_entry,
11338c2ecf20Sopenharmony_ci					  ipip_entry))
11348c2ecf20Sopenharmony_ci		return;
11358c2ecf20Sopenharmony_ci	decap_fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	if (mlxsw_sp_fib_entry_update(mlxsw_sp, decap_fib_entry))
11388c2ecf20Sopenharmony_ci		mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
11398c2ecf20Sopenharmony_ci}
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fib_entry *
11428c2ecf20Sopenharmony_cimlxsw_sp_router_ip2me_fib_entry_find(struct mlxsw_sp *mlxsw_sp, u32 tb_id,
11438c2ecf20Sopenharmony_ci				     enum mlxsw_sp_l3proto proto,
11448c2ecf20Sopenharmony_ci				     const union mlxsw_sp_l3addr *addr,
11458c2ecf20Sopenharmony_ci				     enum mlxsw_sp_fib_entry_type type)
11468c2ecf20Sopenharmony_ci{
11478c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node;
11488c2ecf20Sopenharmony_ci	unsigned char addr_prefix_len;
11498c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib;
11508c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
11518c2ecf20Sopenharmony_ci	const void *addrp;
11528c2ecf20Sopenharmony_ci	size_t addr_len;
11538c2ecf20Sopenharmony_ci	u32 addr4;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
11568c2ecf20Sopenharmony_ci	if (!vr)
11578c2ecf20Sopenharmony_ci		return NULL;
11588c2ecf20Sopenharmony_ci	fib = mlxsw_sp_vr_fib(vr, proto);
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	switch (proto) {
11618c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
11628c2ecf20Sopenharmony_ci		addr4 = be32_to_cpu(addr->addr4);
11638c2ecf20Sopenharmony_ci		addrp = &addr4;
11648c2ecf20Sopenharmony_ci		addr_len = 4;
11658c2ecf20Sopenharmony_ci		addr_prefix_len = 32;
11668c2ecf20Sopenharmony_ci		break;
11678c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
11688c2ecf20Sopenharmony_ci	default:
11698c2ecf20Sopenharmony_ci		WARN_ON(1);
11708c2ecf20Sopenharmony_ci		return NULL;
11718c2ecf20Sopenharmony_ci	}
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	fib_node = mlxsw_sp_fib_node_lookup(fib, addrp, addr_len,
11748c2ecf20Sopenharmony_ci					    addr_prefix_len);
11758c2ecf20Sopenharmony_ci	if (!fib_node || fib_node->fib_entry->type != type)
11768c2ecf20Sopenharmony_ci		return NULL;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	return fib_node->fib_entry;
11798c2ecf20Sopenharmony_ci}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci/* Given an IPIP entry, find the corresponding decap route. */
11828c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fib_entry *
11838c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_find_decap(struct mlxsw_sp *mlxsw_sp,
11848c2ecf20Sopenharmony_ci			       struct mlxsw_sp_ipip_entry *ipip_entry)
11858c2ecf20Sopenharmony_ci{
11868c2ecf20Sopenharmony_ci	static struct mlxsw_sp_fib_node *fib_node;
11878c2ecf20Sopenharmony_ci	const struct mlxsw_sp_ipip_ops *ipip_ops;
11888c2ecf20Sopenharmony_ci	unsigned char saddr_prefix_len;
11898c2ecf20Sopenharmony_ci	union mlxsw_sp_l3addr saddr;
11908c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *ul_fib;
11918c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *ul_vr;
11928c2ecf20Sopenharmony_ci	const void *saddrp;
11938c2ecf20Sopenharmony_ci	size_t saddr_len;
11948c2ecf20Sopenharmony_ci	u32 ul_tb_id;
11958c2ecf20Sopenharmony_ci	u32 saddr4;
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
12008c2ecf20Sopenharmony_ci	ul_vr = mlxsw_sp_vr_find(mlxsw_sp, ul_tb_id);
12018c2ecf20Sopenharmony_ci	if (!ul_vr)
12028c2ecf20Sopenharmony_ci		return NULL;
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	ul_fib = mlxsw_sp_vr_fib(ul_vr, ipip_ops->ul_proto);
12058c2ecf20Sopenharmony_ci	saddr = mlxsw_sp_ipip_netdev_saddr(ipip_ops->ul_proto,
12068c2ecf20Sopenharmony_ci					   ipip_entry->ol_dev);
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	switch (ipip_ops->ul_proto) {
12098c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
12108c2ecf20Sopenharmony_ci		saddr4 = be32_to_cpu(saddr.addr4);
12118c2ecf20Sopenharmony_ci		saddrp = &saddr4;
12128c2ecf20Sopenharmony_ci		saddr_len = 4;
12138c2ecf20Sopenharmony_ci		saddr_prefix_len = 32;
12148c2ecf20Sopenharmony_ci		break;
12158c2ecf20Sopenharmony_ci	default:
12168c2ecf20Sopenharmony_ci		WARN_ON(1);
12178c2ecf20Sopenharmony_ci		return NULL;
12188c2ecf20Sopenharmony_ci	}
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	fib_node = mlxsw_sp_fib_node_lookup(ul_fib, saddrp, saddr_len,
12218c2ecf20Sopenharmony_ci					    saddr_prefix_len);
12228c2ecf20Sopenharmony_ci	if (!fib_node ||
12238c2ecf20Sopenharmony_ci	    fib_node->fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP)
12248c2ecf20Sopenharmony_ci		return NULL;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	return fib_node->fib_entry;
12278c2ecf20Sopenharmony_ci}
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_cistatic struct mlxsw_sp_ipip_entry *
12308c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_create(struct mlxsw_sp *mlxsw_sp,
12318c2ecf20Sopenharmony_ci			   enum mlxsw_sp_ipip_type ipipt,
12328c2ecf20Sopenharmony_ci			   struct net_device *ol_dev)
12338c2ecf20Sopenharmony_ci{
12348c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	ipip_entry = mlxsw_sp_ipip_entry_alloc(mlxsw_sp, ipipt, ol_dev);
12378c2ecf20Sopenharmony_ci	if (IS_ERR(ipip_entry))
12388c2ecf20Sopenharmony_ci		return ipip_entry;
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	list_add_tail(&ipip_entry->ipip_list_node,
12418c2ecf20Sopenharmony_ci		      &mlxsw_sp->router->ipip_list);
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	return ipip_entry;
12448c2ecf20Sopenharmony_ci}
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_cistatic void
12478c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_destroy(struct mlxsw_sp *mlxsw_sp,
12488c2ecf20Sopenharmony_ci			    struct mlxsw_sp_ipip_entry *ipip_entry)
12498c2ecf20Sopenharmony_ci{
12508c2ecf20Sopenharmony_ci	list_del(&ipip_entry->ipip_list_node);
12518c2ecf20Sopenharmony_ci	mlxsw_sp_ipip_entry_dealloc(ipip_entry);
12528c2ecf20Sopenharmony_ci}
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_cistatic bool
12558c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_matches_decap(struct mlxsw_sp *mlxsw_sp,
12568c2ecf20Sopenharmony_ci				  const struct net_device *ul_dev,
12578c2ecf20Sopenharmony_ci				  enum mlxsw_sp_l3proto ul_proto,
12588c2ecf20Sopenharmony_ci				  union mlxsw_sp_l3addr ul_dip,
12598c2ecf20Sopenharmony_ci				  struct mlxsw_sp_ipip_entry *ipip_entry)
12608c2ecf20Sopenharmony_ci{
12618c2ecf20Sopenharmony_ci	u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN;
12628c2ecf20Sopenharmony_ci	enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
12658c2ecf20Sopenharmony_ci		return false;
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	return mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, ul_dip,
12688c2ecf20Sopenharmony_ci						 ul_tb_id, ipip_entry);
12698c2ecf20Sopenharmony_ci}
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci/* Given decap parameters, find the corresponding IPIP entry. */
12728c2ecf20Sopenharmony_cistatic struct mlxsw_sp_ipip_entry *
12738c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_find_by_decap(struct mlxsw_sp *mlxsw_sp,
12748c2ecf20Sopenharmony_ci				  const struct net_device *ul_dev,
12758c2ecf20Sopenharmony_ci				  enum mlxsw_sp_l3proto ul_proto,
12768c2ecf20Sopenharmony_ci				  union mlxsw_sp_l3addr ul_dip)
12778c2ecf20Sopenharmony_ci{
12788c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
12818c2ecf20Sopenharmony_ci			    ipip_list_node)
12828c2ecf20Sopenharmony_ci		if (mlxsw_sp_ipip_entry_matches_decap(mlxsw_sp, ul_dev,
12838c2ecf20Sopenharmony_ci						      ul_proto, ul_dip,
12848c2ecf20Sopenharmony_ci						      ipip_entry))
12858c2ecf20Sopenharmony_ci			return ipip_entry;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	return NULL;
12888c2ecf20Sopenharmony_ci}
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_cistatic bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp,
12918c2ecf20Sopenharmony_ci				      const struct net_device *dev,
12928c2ecf20Sopenharmony_ci				      enum mlxsw_sp_ipip_type *p_type)
12938c2ecf20Sopenharmony_ci{
12948c2ecf20Sopenharmony_ci	struct mlxsw_sp_router *router = mlxsw_sp->router;
12958c2ecf20Sopenharmony_ci	const struct mlxsw_sp_ipip_ops *ipip_ops;
12968c2ecf20Sopenharmony_ci	enum mlxsw_sp_ipip_type ipipt;
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	for (ipipt = 0; ipipt < MLXSW_SP_IPIP_TYPE_MAX; ++ipipt) {
12998c2ecf20Sopenharmony_ci		ipip_ops = router->ipip_ops_arr[ipipt];
13008c2ecf20Sopenharmony_ci		if (dev->type == ipip_ops->dev_type) {
13018c2ecf20Sopenharmony_ci			if (p_type)
13028c2ecf20Sopenharmony_ci				*p_type = ipipt;
13038c2ecf20Sopenharmony_ci			return true;
13048c2ecf20Sopenharmony_ci		}
13058c2ecf20Sopenharmony_ci	}
13068c2ecf20Sopenharmony_ci	return false;
13078c2ecf20Sopenharmony_ci}
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_cibool mlxsw_sp_netdev_is_ipip_ol(const struct mlxsw_sp *mlxsw_sp,
13108c2ecf20Sopenharmony_ci				const struct net_device *dev)
13118c2ecf20Sopenharmony_ci{
13128c2ecf20Sopenharmony_ci	return mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL);
13138c2ecf20Sopenharmony_ci}
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_cistatic struct mlxsw_sp_ipip_entry *
13168c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_find_by_ol_dev(struct mlxsw_sp *mlxsw_sp,
13178c2ecf20Sopenharmony_ci				   const struct net_device *ol_dev)
13188c2ecf20Sopenharmony_ci{
13198c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
13228c2ecf20Sopenharmony_ci			    ipip_list_node)
13238c2ecf20Sopenharmony_ci		if (ipip_entry->ol_dev == ol_dev)
13248c2ecf20Sopenharmony_ci			return ipip_entry;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	return NULL;
13278c2ecf20Sopenharmony_ci}
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_cistatic struct mlxsw_sp_ipip_entry *
13308c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_find_by_ul_dev(const struct mlxsw_sp *mlxsw_sp,
13318c2ecf20Sopenharmony_ci				   const struct net_device *ul_dev,
13328c2ecf20Sopenharmony_ci				   struct mlxsw_sp_ipip_entry *start)
13338c2ecf20Sopenharmony_ci{
13348c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	ipip_entry = list_prepare_entry(start, &mlxsw_sp->router->ipip_list,
13378c2ecf20Sopenharmony_ci					ipip_list_node);
13388c2ecf20Sopenharmony_ci	list_for_each_entry_continue(ipip_entry, &mlxsw_sp->router->ipip_list,
13398c2ecf20Sopenharmony_ci				     ipip_list_node) {
13408c2ecf20Sopenharmony_ci		struct net_device *ol_dev = ipip_entry->ol_dev;
13418c2ecf20Sopenharmony_ci		struct net_device *ipip_ul_dev;
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci		rcu_read_lock();
13448c2ecf20Sopenharmony_ci		ipip_ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);
13458c2ecf20Sopenharmony_ci		rcu_read_unlock();
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci		if (ipip_ul_dev == ul_dev)
13488c2ecf20Sopenharmony_ci			return ipip_entry;
13498c2ecf20Sopenharmony_ci	}
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	return NULL;
13528c2ecf20Sopenharmony_ci}
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_cibool mlxsw_sp_netdev_is_ipip_ul(struct mlxsw_sp *mlxsw_sp,
13558c2ecf20Sopenharmony_ci				const struct net_device *dev)
13568c2ecf20Sopenharmony_ci{
13578c2ecf20Sopenharmony_ci	bool is_ipip_ul;
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
13608c2ecf20Sopenharmony_ci	is_ipip_ul = mlxsw_sp_ipip_entry_find_by_ul_dev(mlxsw_sp, dev, NULL);
13618c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	return is_ipip_ul;
13648c2ecf20Sopenharmony_ci}
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_cistatic bool mlxsw_sp_netdevice_ipip_can_offload(struct mlxsw_sp *mlxsw_sp,
13678c2ecf20Sopenharmony_ci						const struct net_device *ol_dev,
13688c2ecf20Sopenharmony_ci						enum mlxsw_sp_ipip_type ipipt)
13698c2ecf20Sopenharmony_ci{
13708c2ecf20Sopenharmony_ci	const struct mlxsw_sp_ipip_ops *ops
13718c2ecf20Sopenharmony_ci		= mlxsw_sp->router->ipip_ops_arr[ipipt];
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	/* For deciding whether decap should be offloaded, we don't care about
13748c2ecf20Sopenharmony_ci	 * overlay protocol, so ask whether either one is supported.
13758c2ecf20Sopenharmony_ci	 */
13768c2ecf20Sopenharmony_ci	return ops->can_offload(mlxsw_sp, ol_dev, MLXSW_SP_L3_PROTO_IPV4) ||
13778c2ecf20Sopenharmony_ci	       ops->can_offload(mlxsw_sp, ol_dev, MLXSW_SP_L3_PROTO_IPV6);
13788c2ecf20Sopenharmony_ci}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_cistatic int mlxsw_sp_netdevice_ipip_ol_reg_event(struct mlxsw_sp *mlxsw_sp,
13818c2ecf20Sopenharmony_ci						struct net_device *ol_dev)
13828c2ecf20Sopenharmony_ci{
13838c2ecf20Sopenharmony_ci	enum mlxsw_sp_ipip_type ipipt = MLXSW_SP_IPIP_TYPE_MAX;
13848c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
13858c2ecf20Sopenharmony_ci	enum mlxsw_sp_l3proto ul_proto;
13868c2ecf20Sopenharmony_ci	union mlxsw_sp_l3addr saddr;
13878c2ecf20Sopenharmony_ci	u32 ul_tb_id;
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	mlxsw_sp_netdev_ipip_type(mlxsw_sp, ol_dev, &ipipt);
13908c2ecf20Sopenharmony_ci	if (mlxsw_sp_netdevice_ipip_can_offload(mlxsw_sp, ol_dev, ipipt)) {
13918c2ecf20Sopenharmony_ci		ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev);
13928c2ecf20Sopenharmony_ci		ul_proto = mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto;
13938c2ecf20Sopenharmony_ci		saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ol_dev);
13948c2ecf20Sopenharmony_ci		if (!mlxsw_sp_ipip_demote_tunnel_by_saddr(mlxsw_sp, ul_proto,
13958c2ecf20Sopenharmony_ci							  saddr, ul_tb_id,
13968c2ecf20Sopenharmony_ci							  NULL)) {
13978c2ecf20Sopenharmony_ci			ipip_entry = mlxsw_sp_ipip_entry_create(mlxsw_sp, ipipt,
13988c2ecf20Sopenharmony_ci								ol_dev);
13998c2ecf20Sopenharmony_ci			if (IS_ERR(ipip_entry))
14008c2ecf20Sopenharmony_ci				return PTR_ERR(ipip_entry);
14018c2ecf20Sopenharmony_ci		}
14028c2ecf20Sopenharmony_ci	}
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	return 0;
14058c2ecf20Sopenharmony_ci}
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_cistatic void mlxsw_sp_netdevice_ipip_ol_unreg_event(struct mlxsw_sp *mlxsw_sp,
14088c2ecf20Sopenharmony_ci						   struct net_device *ol_dev)
14098c2ecf20Sopenharmony_ci{
14108c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);
14138c2ecf20Sopenharmony_ci	if (ipip_entry)
14148c2ecf20Sopenharmony_ci		mlxsw_sp_ipip_entry_destroy(mlxsw_sp, ipip_entry);
14158c2ecf20Sopenharmony_ci}
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_cistatic void
14188c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_ol_up_event(struct mlxsw_sp *mlxsw_sp,
14198c2ecf20Sopenharmony_ci				struct mlxsw_sp_ipip_entry *ipip_entry)
14208c2ecf20Sopenharmony_ci{
14218c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_entry *decap_fib_entry;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	decap_fib_entry = mlxsw_sp_ipip_entry_find_decap(mlxsw_sp, ipip_entry);
14248c2ecf20Sopenharmony_ci	if (decap_fib_entry)
14258c2ecf20Sopenharmony_ci		mlxsw_sp_ipip_entry_promote_decap(mlxsw_sp, ipip_entry,
14268c2ecf20Sopenharmony_ci						  decap_fib_entry);
14278c2ecf20Sopenharmony_ci}
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_cistatic int
14308c2ecf20Sopenharmony_cimlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif, u16 ul_vr_id,
14318c2ecf20Sopenharmony_ci			u16 ul_rif_id, bool enable)
14328c2ecf20Sopenharmony_ci{
14338c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config;
14348c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif = &lb_rif->common;
14358c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
14368c2ecf20Sopenharmony_ci	char ritr_pl[MLXSW_REG_RITR_LEN];
14378c2ecf20Sopenharmony_ci	u32 saddr4;
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	switch (lb_cf.ul_protocol) {
14408c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
14418c2ecf20Sopenharmony_ci		saddr4 = be32_to_cpu(lb_cf.saddr.addr4);
14428c2ecf20Sopenharmony_ci		mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,
14438c2ecf20Sopenharmony_ci				    rif->rif_index, rif->vr_id, rif->dev->mtu);
14448c2ecf20Sopenharmony_ci		mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt,
14458c2ecf20Sopenharmony_ci			    MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET,
14468c2ecf20Sopenharmony_ci			    ul_vr_id, ul_rif_id, saddr4, lb_cf.okey);
14478c2ecf20Sopenharmony_ci		break;
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
14508c2ecf20Sopenharmony_ci		return -EAFNOSUPPORT;
14518c2ecf20Sopenharmony_ci	}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
14548c2ecf20Sopenharmony_ci}
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_cistatic int mlxsw_sp_netdevice_ipip_ol_update_mtu(struct mlxsw_sp *mlxsw_sp,
14578c2ecf20Sopenharmony_ci						 struct net_device *ol_dev)
14588c2ecf20Sopenharmony_ci{
14598c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
14608c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_ipip_lb *lb_rif;
14618c2ecf20Sopenharmony_ci	int err = 0;
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);
14648c2ecf20Sopenharmony_ci	if (ipip_entry) {
14658c2ecf20Sopenharmony_ci		lb_rif = ipip_entry->ol_lb;
14668c2ecf20Sopenharmony_ci		err = mlxsw_sp_rif_ipip_lb_op(lb_rif, lb_rif->ul_vr_id,
14678c2ecf20Sopenharmony_ci					      lb_rif->ul_rif_id, true);
14688c2ecf20Sopenharmony_ci		if (err)
14698c2ecf20Sopenharmony_ci			goto out;
14708c2ecf20Sopenharmony_ci		lb_rif->common.mtu = ol_dev->mtu;
14718c2ecf20Sopenharmony_ci	}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ciout:
14748c2ecf20Sopenharmony_ci	return err;
14758c2ecf20Sopenharmony_ci}
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_cistatic void mlxsw_sp_netdevice_ipip_ol_up_event(struct mlxsw_sp *mlxsw_sp,
14788c2ecf20Sopenharmony_ci						struct net_device *ol_dev)
14798c2ecf20Sopenharmony_ci{
14808c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci	ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);
14838c2ecf20Sopenharmony_ci	if (ipip_entry)
14848c2ecf20Sopenharmony_ci		mlxsw_sp_ipip_entry_ol_up_event(mlxsw_sp, ipip_entry);
14858c2ecf20Sopenharmony_ci}
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_cistatic void
14888c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_ol_down_event(struct mlxsw_sp *mlxsw_sp,
14898c2ecf20Sopenharmony_ci				  struct mlxsw_sp_ipip_entry *ipip_entry)
14908c2ecf20Sopenharmony_ci{
14918c2ecf20Sopenharmony_ci	if (ipip_entry->decap_fib_entry)
14928c2ecf20Sopenharmony_ci		mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
14938c2ecf20Sopenharmony_ci}
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_cistatic void mlxsw_sp_netdevice_ipip_ol_down_event(struct mlxsw_sp *mlxsw_sp,
14968c2ecf20Sopenharmony_ci						  struct net_device *ol_dev)
14978c2ecf20Sopenharmony_ci{
14988c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);
15018c2ecf20Sopenharmony_ci	if (ipip_entry)
15028c2ecf20Sopenharmony_ci		mlxsw_sp_ipip_entry_ol_down_event(mlxsw_sp, ipip_entry);
15038c2ecf20Sopenharmony_ci}
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop_rif_migrate(struct mlxsw_sp *mlxsw_sp,
15068c2ecf20Sopenharmony_ci					 struct mlxsw_sp_rif *old_rif,
15078c2ecf20Sopenharmony_ci					 struct mlxsw_sp_rif *new_rif);
15088c2ecf20Sopenharmony_cistatic int
15098c2ecf20Sopenharmony_cimlxsw_sp_ipip_entry_ol_lb_update(struct mlxsw_sp *mlxsw_sp,
15108c2ecf20Sopenharmony_ci				 struct mlxsw_sp_ipip_entry *ipip_entry,
15118c2ecf20Sopenharmony_ci				 bool keep_encap,
15128c2ecf20Sopenharmony_ci				 struct netlink_ext_ack *extack)
15138c2ecf20Sopenharmony_ci{
15148c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_ipip_lb *old_lb_rif = ipip_entry->ol_lb;
15158c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_ipip_lb *new_lb_rif;
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	new_lb_rif = mlxsw_sp_ipip_ol_ipip_lb_create(mlxsw_sp,
15188c2ecf20Sopenharmony_ci						     ipip_entry->ipipt,
15198c2ecf20Sopenharmony_ci						     ipip_entry->ol_dev,
15208c2ecf20Sopenharmony_ci						     extack);
15218c2ecf20Sopenharmony_ci	if (IS_ERR(new_lb_rif))
15228c2ecf20Sopenharmony_ci		return PTR_ERR(new_lb_rif);
15238c2ecf20Sopenharmony_ci	ipip_entry->ol_lb = new_lb_rif;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	if (keep_encap)
15268c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop_rif_migrate(mlxsw_sp, &old_lb_rif->common,
15278c2ecf20Sopenharmony_ci					     &new_lb_rif->common);
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	mlxsw_sp_rif_destroy(&old_lb_rif->common);
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	return 0;
15328c2ecf20Sopenharmony_ci}
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop_rif_update(struct mlxsw_sp *mlxsw_sp,
15358c2ecf20Sopenharmony_ci					struct mlxsw_sp_rif *rif);
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci/**
15388c2ecf20Sopenharmony_ci * __mlxsw_sp_ipip_entry_update_tunnel - Update offload related to IPIP entry.
15398c2ecf20Sopenharmony_ci * @mlxsw_sp: mlxsw_sp.
15408c2ecf20Sopenharmony_ci * @ipip_entry: IPIP entry.
15418c2ecf20Sopenharmony_ci * @recreate_loopback: Recreates the associated loopback RIF.
15428c2ecf20Sopenharmony_ci * @keep_encap: Updates next hops that use the tunnel netdevice. This is only
15438c2ecf20Sopenharmony_ci *              relevant when recreate_loopback is true.
15448c2ecf20Sopenharmony_ci * @update_nexthops: Updates next hops, keeping the current loopback RIF. This
15458c2ecf20Sopenharmony_ci *                   is only relevant when recreate_loopback is false.
15468c2ecf20Sopenharmony_ci * @extack: extack.
15478c2ecf20Sopenharmony_ci *
15488c2ecf20Sopenharmony_ci * Return: Non-zero value on failure.
15498c2ecf20Sopenharmony_ci */
15508c2ecf20Sopenharmony_ciint __mlxsw_sp_ipip_entry_update_tunnel(struct mlxsw_sp *mlxsw_sp,
15518c2ecf20Sopenharmony_ci					struct mlxsw_sp_ipip_entry *ipip_entry,
15528c2ecf20Sopenharmony_ci					bool recreate_loopback,
15538c2ecf20Sopenharmony_ci					bool keep_encap,
15548c2ecf20Sopenharmony_ci					bool update_nexthops,
15558c2ecf20Sopenharmony_ci					struct netlink_ext_ack *extack)
15568c2ecf20Sopenharmony_ci{
15578c2ecf20Sopenharmony_ci	int err;
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	/* RIFs can't be edited, so to update loopback, we need to destroy and
15608c2ecf20Sopenharmony_ci	 * recreate it. That creates a window of opportunity where RALUE and
15618c2ecf20Sopenharmony_ci	 * RATR registers end up referencing a RIF that's already gone. RATRs
15628c2ecf20Sopenharmony_ci	 * are handled in mlxsw_sp_ipip_entry_ol_lb_update(), and to take care
15638c2ecf20Sopenharmony_ci	 * of RALUE, demote the decap route back.
15648c2ecf20Sopenharmony_ci	 */
15658c2ecf20Sopenharmony_ci	if (ipip_entry->decap_fib_entry)
15668c2ecf20Sopenharmony_ci		mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci	if (recreate_loopback) {
15698c2ecf20Sopenharmony_ci		err = mlxsw_sp_ipip_entry_ol_lb_update(mlxsw_sp, ipip_entry,
15708c2ecf20Sopenharmony_ci						       keep_encap, extack);
15718c2ecf20Sopenharmony_ci		if (err)
15728c2ecf20Sopenharmony_ci			return err;
15738c2ecf20Sopenharmony_ci	} else if (update_nexthops) {
15748c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop_rif_update(mlxsw_sp,
15758c2ecf20Sopenharmony_ci					    &ipip_entry->ol_lb->common);
15768c2ecf20Sopenharmony_ci	}
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci	if (ipip_entry->ol_dev->flags & IFF_UP)
15798c2ecf20Sopenharmony_ci		mlxsw_sp_ipip_entry_ol_up_event(mlxsw_sp, ipip_entry);
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	return 0;
15828c2ecf20Sopenharmony_ci}
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_cistatic int mlxsw_sp_netdevice_ipip_ol_vrf_event(struct mlxsw_sp *mlxsw_sp,
15858c2ecf20Sopenharmony_ci						struct net_device *ol_dev,
15868c2ecf20Sopenharmony_ci						struct netlink_ext_ack *extack)
15878c2ecf20Sopenharmony_ci{
15888c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry =
15898c2ecf20Sopenharmony_ci		mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	if (!ipip_entry)
15928c2ecf20Sopenharmony_ci		return 0;
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci	return __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry,
15958c2ecf20Sopenharmony_ci						   true, false, false, extack);
15968c2ecf20Sopenharmony_ci}
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_cistatic int
15998c2ecf20Sopenharmony_cimlxsw_sp_netdevice_ipip_ul_vrf_event(struct mlxsw_sp *mlxsw_sp,
16008c2ecf20Sopenharmony_ci				     struct mlxsw_sp_ipip_entry *ipip_entry,
16018c2ecf20Sopenharmony_ci				     struct net_device *ul_dev,
16028c2ecf20Sopenharmony_ci				     bool *demote_this,
16038c2ecf20Sopenharmony_ci				     struct netlink_ext_ack *extack)
16048c2ecf20Sopenharmony_ci{
16058c2ecf20Sopenharmony_ci	u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN;
16068c2ecf20Sopenharmony_ci	enum mlxsw_sp_l3proto ul_proto;
16078c2ecf20Sopenharmony_ci	union mlxsw_sp_l3addr saddr;
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci	/* Moving underlay to a different VRF might cause local address
16108c2ecf20Sopenharmony_ci	 * conflict, and the conflicting tunnels need to be demoted.
16118c2ecf20Sopenharmony_ci	 */
16128c2ecf20Sopenharmony_ci	ul_proto = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt]->ul_proto;
16138c2ecf20Sopenharmony_ci	saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ipip_entry->ol_dev);
16148c2ecf20Sopenharmony_ci	if (mlxsw_sp_ipip_demote_tunnel_by_saddr(mlxsw_sp, ul_proto,
16158c2ecf20Sopenharmony_ci						 saddr, ul_tb_id,
16168c2ecf20Sopenharmony_ci						 ipip_entry)) {
16178c2ecf20Sopenharmony_ci		*demote_this = true;
16188c2ecf20Sopenharmony_ci		return 0;
16198c2ecf20Sopenharmony_ci	}
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	return __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry,
16228c2ecf20Sopenharmony_ci						   true, true, false, extack);
16238c2ecf20Sopenharmony_ci}
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_cistatic int
16268c2ecf20Sopenharmony_cimlxsw_sp_netdevice_ipip_ul_up_event(struct mlxsw_sp *mlxsw_sp,
16278c2ecf20Sopenharmony_ci				    struct mlxsw_sp_ipip_entry *ipip_entry,
16288c2ecf20Sopenharmony_ci				    struct net_device *ul_dev)
16298c2ecf20Sopenharmony_ci{
16308c2ecf20Sopenharmony_ci	return __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry,
16318c2ecf20Sopenharmony_ci						   false, false, true, NULL);
16328c2ecf20Sopenharmony_ci}
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_cistatic int
16358c2ecf20Sopenharmony_cimlxsw_sp_netdevice_ipip_ul_down_event(struct mlxsw_sp *mlxsw_sp,
16368c2ecf20Sopenharmony_ci				      struct mlxsw_sp_ipip_entry *ipip_entry,
16378c2ecf20Sopenharmony_ci				      struct net_device *ul_dev)
16388c2ecf20Sopenharmony_ci{
16398c2ecf20Sopenharmony_ci	/* A down underlay device causes encapsulated packets to not be
16408c2ecf20Sopenharmony_ci	 * forwarded, but decap still works. So refresh next hops without
16418c2ecf20Sopenharmony_ci	 * touching anything else.
16428c2ecf20Sopenharmony_ci	 */
16438c2ecf20Sopenharmony_ci	return __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry,
16448c2ecf20Sopenharmony_ci						   false, false, true, NULL);
16458c2ecf20Sopenharmony_ci}
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_cistatic int
16488c2ecf20Sopenharmony_cimlxsw_sp_netdevice_ipip_ol_change_event(struct mlxsw_sp *mlxsw_sp,
16498c2ecf20Sopenharmony_ci					struct net_device *ol_dev,
16508c2ecf20Sopenharmony_ci					struct netlink_ext_ack *extack)
16518c2ecf20Sopenharmony_ci{
16528c2ecf20Sopenharmony_ci	const struct mlxsw_sp_ipip_ops *ipip_ops;
16538c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
16548c2ecf20Sopenharmony_ci	int err;
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ci	ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);
16578c2ecf20Sopenharmony_ci	if (!ipip_entry)
16588c2ecf20Sopenharmony_ci		/* A change might make a tunnel eligible for offloading, but
16598c2ecf20Sopenharmony_ci		 * that is currently not implemented. What falls to slow path
16608c2ecf20Sopenharmony_ci		 * stays there.
16618c2ecf20Sopenharmony_ci		 */
16628c2ecf20Sopenharmony_ci		return 0;
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci	/* A change might make a tunnel not eligible for offloading. */
16658c2ecf20Sopenharmony_ci	if (!mlxsw_sp_netdevice_ipip_can_offload(mlxsw_sp, ol_dev,
16668c2ecf20Sopenharmony_ci						 ipip_entry->ipipt)) {
16678c2ecf20Sopenharmony_ci		mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry);
16688c2ecf20Sopenharmony_ci		return 0;
16698c2ecf20Sopenharmony_ci	}
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci	ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
16728c2ecf20Sopenharmony_ci	err = ipip_ops->ol_netdev_change(mlxsw_sp, ipip_entry, extack);
16738c2ecf20Sopenharmony_ci	return err;
16748c2ecf20Sopenharmony_ci}
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_civoid mlxsw_sp_ipip_entry_demote_tunnel(struct mlxsw_sp *mlxsw_sp,
16778c2ecf20Sopenharmony_ci				       struct mlxsw_sp_ipip_entry *ipip_entry)
16788c2ecf20Sopenharmony_ci{
16798c2ecf20Sopenharmony_ci	struct net_device *ol_dev = ipip_entry->ol_dev;
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	if (ol_dev->flags & IFF_UP)
16828c2ecf20Sopenharmony_ci		mlxsw_sp_ipip_entry_ol_down_event(mlxsw_sp, ipip_entry);
16838c2ecf20Sopenharmony_ci	mlxsw_sp_ipip_entry_destroy(mlxsw_sp, ipip_entry);
16848c2ecf20Sopenharmony_ci}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci/* The configuration where several tunnels have the same local address in the
16878c2ecf20Sopenharmony_ci * same underlay table needs special treatment in the HW. That is currently not
16888c2ecf20Sopenharmony_ci * implemented in the driver. This function finds and demotes the first tunnel
16898c2ecf20Sopenharmony_ci * with a given source address, except the one passed in in the argument
16908c2ecf20Sopenharmony_ci * `except'.
16918c2ecf20Sopenharmony_ci */
16928c2ecf20Sopenharmony_cibool
16938c2ecf20Sopenharmony_cimlxsw_sp_ipip_demote_tunnel_by_saddr(struct mlxsw_sp *mlxsw_sp,
16948c2ecf20Sopenharmony_ci				     enum mlxsw_sp_l3proto ul_proto,
16958c2ecf20Sopenharmony_ci				     union mlxsw_sp_l3addr saddr,
16968c2ecf20Sopenharmony_ci				     u32 ul_tb_id,
16978c2ecf20Sopenharmony_ci				     const struct mlxsw_sp_ipip_entry *except)
16988c2ecf20Sopenharmony_ci{
16998c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry, *tmp;
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	list_for_each_entry_safe(ipip_entry, tmp, &mlxsw_sp->router->ipip_list,
17028c2ecf20Sopenharmony_ci				 ipip_list_node) {
17038c2ecf20Sopenharmony_ci		if (ipip_entry != except &&
17048c2ecf20Sopenharmony_ci		    mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, saddr,
17058c2ecf20Sopenharmony_ci						      ul_tb_id, ipip_entry)) {
17068c2ecf20Sopenharmony_ci			mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry);
17078c2ecf20Sopenharmony_ci			return true;
17088c2ecf20Sopenharmony_ci		}
17098c2ecf20Sopenharmony_ci	}
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci	return false;
17128c2ecf20Sopenharmony_ci}
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_cistatic void mlxsw_sp_ipip_demote_tunnel_by_ul_netdev(struct mlxsw_sp *mlxsw_sp,
17158c2ecf20Sopenharmony_ci						     struct net_device *ul_dev)
17168c2ecf20Sopenharmony_ci{
17178c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry, *tmp;
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	list_for_each_entry_safe(ipip_entry, tmp, &mlxsw_sp->router->ipip_list,
17208c2ecf20Sopenharmony_ci				 ipip_list_node) {
17218c2ecf20Sopenharmony_ci		struct net_device *ol_dev = ipip_entry->ol_dev;
17228c2ecf20Sopenharmony_ci		struct net_device *ipip_ul_dev;
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci		rcu_read_lock();
17258c2ecf20Sopenharmony_ci		ipip_ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);
17268c2ecf20Sopenharmony_ci		rcu_read_unlock();
17278c2ecf20Sopenharmony_ci		if (ipip_ul_dev == ul_dev)
17288c2ecf20Sopenharmony_ci			mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry);
17298c2ecf20Sopenharmony_ci	}
17308c2ecf20Sopenharmony_ci}
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ciint mlxsw_sp_netdevice_ipip_ol_event(struct mlxsw_sp *mlxsw_sp,
17338c2ecf20Sopenharmony_ci				     struct net_device *ol_dev,
17348c2ecf20Sopenharmony_ci				     unsigned long event,
17358c2ecf20Sopenharmony_ci				     struct netdev_notifier_info *info)
17368c2ecf20Sopenharmony_ci{
17378c2ecf20Sopenharmony_ci	struct netdev_notifier_changeupper_info *chup;
17388c2ecf20Sopenharmony_ci	struct netlink_ext_ack *extack;
17398c2ecf20Sopenharmony_ci	int err = 0;
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
17428c2ecf20Sopenharmony_ci	switch (event) {
17438c2ecf20Sopenharmony_ci	case NETDEV_REGISTER:
17448c2ecf20Sopenharmony_ci		err = mlxsw_sp_netdevice_ipip_ol_reg_event(mlxsw_sp, ol_dev);
17458c2ecf20Sopenharmony_ci		break;
17468c2ecf20Sopenharmony_ci	case NETDEV_UNREGISTER:
17478c2ecf20Sopenharmony_ci		mlxsw_sp_netdevice_ipip_ol_unreg_event(mlxsw_sp, ol_dev);
17488c2ecf20Sopenharmony_ci		break;
17498c2ecf20Sopenharmony_ci	case NETDEV_UP:
17508c2ecf20Sopenharmony_ci		mlxsw_sp_netdevice_ipip_ol_up_event(mlxsw_sp, ol_dev);
17518c2ecf20Sopenharmony_ci		break;
17528c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
17538c2ecf20Sopenharmony_ci		mlxsw_sp_netdevice_ipip_ol_down_event(mlxsw_sp, ol_dev);
17548c2ecf20Sopenharmony_ci		break;
17558c2ecf20Sopenharmony_ci	case NETDEV_CHANGEUPPER:
17568c2ecf20Sopenharmony_ci		chup = container_of(info, typeof(*chup), info);
17578c2ecf20Sopenharmony_ci		extack = info->extack;
17588c2ecf20Sopenharmony_ci		if (netif_is_l3_master(chup->upper_dev))
17598c2ecf20Sopenharmony_ci			err = mlxsw_sp_netdevice_ipip_ol_vrf_event(mlxsw_sp,
17608c2ecf20Sopenharmony_ci								   ol_dev,
17618c2ecf20Sopenharmony_ci								   extack);
17628c2ecf20Sopenharmony_ci		break;
17638c2ecf20Sopenharmony_ci	case NETDEV_CHANGE:
17648c2ecf20Sopenharmony_ci		extack = info->extack;
17658c2ecf20Sopenharmony_ci		err = mlxsw_sp_netdevice_ipip_ol_change_event(mlxsw_sp,
17668c2ecf20Sopenharmony_ci							      ol_dev, extack);
17678c2ecf20Sopenharmony_ci		break;
17688c2ecf20Sopenharmony_ci	case NETDEV_CHANGEMTU:
17698c2ecf20Sopenharmony_ci		err = mlxsw_sp_netdevice_ipip_ol_update_mtu(mlxsw_sp, ol_dev);
17708c2ecf20Sopenharmony_ci		break;
17718c2ecf20Sopenharmony_ci	}
17728c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
17738c2ecf20Sopenharmony_ci	return err;
17748c2ecf20Sopenharmony_ci}
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_cistatic int
17778c2ecf20Sopenharmony_ci__mlxsw_sp_netdevice_ipip_ul_event(struct mlxsw_sp *mlxsw_sp,
17788c2ecf20Sopenharmony_ci				   struct mlxsw_sp_ipip_entry *ipip_entry,
17798c2ecf20Sopenharmony_ci				   struct net_device *ul_dev,
17808c2ecf20Sopenharmony_ci				   bool *demote_this,
17818c2ecf20Sopenharmony_ci				   unsigned long event,
17828c2ecf20Sopenharmony_ci				   struct netdev_notifier_info *info)
17838c2ecf20Sopenharmony_ci{
17848c2ecf20Sopenharmony_ci	struct netdev_notifier_changeupper_info *chup;
17858c2ecf20Sopenharmony_ci	struct netlink_ext_ack *extack;
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci	switch (event) {
17888c2ecf20Sopenharmony_ci	case NETDEV_CHANGEUPPER:
17898c2ecf20Sopenharmony_ci		chup = container_of(info, typeof(*chup), info);
17908c2ecf20Sopenharmony_ci		extack = info->extack;
17918c2ecf20Sopenharmony_ci		if (netif_is_l3_master(chup->upper_dev))
17928c2ecf20Sopenharmony_ci			return mlxsw_sp_netdevice_ipip_ul_vrf_event(mlxsw_sp,
17938c2ecf20Sopenharmony_ci								    ipip_entry,
17948c2ecf20Sopenharmony_ci								    ul_dev,
17958c2ecf20Sopenharmony_ci								    demote_this,
17968c2ecf20Sopenharmony_ci								    extack);
17978c2ecf20Sopenharmony_ci		break;
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	case NETDEV_UP:
18008c2ecf20Sopenharmony_ci		return mlxsw_sp_netdevice_ipip_ul_up_event(mlxsw_sp, ipip_entry,
18018c2ecf20Sopenharmony_ci							   ul_dev);
18028c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
18038c2ecf20Sopenharmony_ci		return mlxsw_sp_netdevice_ipip_ul_down_event(mlxsw_sp,
18048c2ecf20Sopenharmony_ci							     ipip_entry,
18058c2ecf20Sopenharmony_ci							     ul_dev);
18068c2ecf20Sopenharmony_ci	}
18078c2ecf20Sopenharmony_ci	return 0;
18088c2ecf20Sopenharmony_ci}
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ciint
18118c2ecf20Sopenharmony_cimlxsw_sp_netdevice_ipip_ul_event(struct mlxsw_sp *mlxsw_sp,
18128c2ecf20Sopenharmony_ci				 struct net_device *ul_dev,
18138c2ecf20Sopenharmony_ci				 unsigned long event,
18148c2ecf20Sopenharmony_ci				 struct netdev_notifier_info *info)
18158c2ecf20Sopenharmony_ci{
18168c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry = NULL;
18178c2ecf20Sopenharmony_ci	int err = 0;
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
18208c2ecf20Sopenharmony_ci	while ((ipip_entry = mlxsw_sp_ipip_entry_find_by_ul_dev(mlxsw_sp,
18218c2ecf20Sopenharmony_ci								ul_dev,
18228c2ecf20Sopenharmony_ci								ipip_entry))) {
18238c2ecf20Sopenharmony_ci		struct mlxsw_sp_ipip_entry *prev;
18248c2ecf20Sopenharmony_ci		bool demote_this = false;
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci		err = __mlxsw_sp_netdevice_ipip_ul_event(mlxsw_sp, ipip_entry,
18278c2ecf20Sopenharmony_ci							 ul_dev, &demote_this,
18288c2ecf20Sopenharmony_ci							 event, info);
18298c2ecf20Sopenharmony_ci		if (err) {
18308c2ecf20Sopenharmony_ci			mlxsw_sp_ipip_demote_tunnel_by_ul_netdev(mlxsw_sp,
18318c2ecf20Sopenharmony_ci								 ul_dev);
18328c2ecf20Sopenharmony_ci			break;
18338c2ecf20Sopenharmony_ci		}
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci		if (demote_this) {
18368c2ecf20Sopenharmony_ci			if (list_is_first(&ipip_entry->ipip_list_node,
18378c2ecf20Sopenharmony_ci					  &mlxsw_sp->router->ipip_list))
18388c2ecf20Sopenharmony_ci				prev = NULL;
18398c2ecf20Sopenharmony_ci			else
18408c2ecf20Sopenharmony_ci				/* This can't be cached from previous iteration,
18418c2ecf20Sopenharmony_ci				 * because that entry could be gone now.
18428c2ecf20Sopenharmony_ci				 */
18438c2ecf20Sopenharmony_ci				prev = list_prev_entry(ipip_entry,
18448c2ecf20Sopenharmony_ci						       ipip_list_node);
18458c2ecf20Sopenharmony_ci			mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry);
18468c2ecf20Sopenharmony_ci			ipip_entry = prev;
18478c2ecf20Sopenharmony_ci		}
18488c2ecf20Sopenharmony_ci	}
18498c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	return err;
18528c2ecf20Sopenharmony_ci}
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ciint mlxsw_sp_router_nve_promote_decap(struct mlxsw_sp *mlxsw_sp, u32 ul_tb_id,
18558c2ecf20Sopenharmony_ci				      enum mlxsw_sp_l3proto ul_proto,
18568c2ecf20Sopenharmony_ci				      const union mlxsw_sp_l3addr *ul_sip,
18578c2ecf20Sopenharmony_ci				      u32 tunnel_index)
18588c2ecf20Sopenharmony_ci{
18598c2ecf20Sopenharmony_ci	enum mlxsw_sp_fib_entry_type type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
18608c2ecf20Sopenharmony_ci	struct mlxsw_sp_router *router = mlxsw_sp->router;
18618c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_entry *fib_entry;
18628c2ecf20Sopenharmony_ci	int err = 0;
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(router->nve_decap_config.valid)) {
18678c2ecf20Sopenharmony_ci		err = -EINVAL;
18688c2ecf20Sopenharmony_ci		goto out;
18698c2ecf20Sopenharmony_ci	}
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	router->nve_decap_config.ul_tb_id = ul_tb_id;
18728c2ecf20Sopenharmony_ci	router->nve_decap_config.tunnel_index = tunnel_index;
18738c2ecf20Sopenharmony_ci	router->nve_decap_config.ul_proto = ul_proto;
18748c2ecf20Sopenharmony_ci	router->nve_decap_config.ul_sip = *ul_sip;
18758c2ecf20Sopenharmony_ci	router->nve_decap_config.valid = true;
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_ci	/* It is valid to create a tunnel with a local IP and only later
18788c2ecf20Sopenharmony_ci	 * assign this IP address to a local interface
18798c2ecf20Sopenharmony_ci	 */
18808c2ecf20Sopenharmony_ci	fib_entry = mlxsw_sp_router_ip2me_fib_entry_find(mlxsw_sp, ul_tb_id,
18818c2ecf20Sopenharmony_ci							 ul_proto, ul_sip,
18828c2ecf20Sopenharmony_ci							 type);
18838c2ecf20Sopenharmony_ci	if (!fib_entry)
18848c2ecf20Sopenharmony_ci		goto out;
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci	fib_entry->decap.tunnel_index = tunnel_index;
18878c2ecf20Sopenharmony_ci	fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP;
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_ci	err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
18908c2ecf20Sopenharmony_ci	if (err)
18918c2ecf20Sopenharmony_ci		goto err_fib_entry_update;
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci	goto out;
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_cierr_fib_entry_update:
18968c2ecf20Sopenharmony_ci	fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
18978c2ecf20Sopenharmony_ci	mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
18988c2ecf20Sopenharmony_ciout:
18998c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
19008c2ecf20Sopenharmony_ci	return err;
19018c2ecf20Sopenharmony_ci}
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_civoid mlxsw_sp_router_nve_demote_decap(struct mlxsw_sp *mlxsw_sp, u32 ul_tb_id,
19048c2ecf20Sopenharmony_ci				      enum mlxsw_sp_l3proto ul_proto,
19058c2ecf20Sopenharmony_ci				      const union mlxsw_sp_l3addr *ul_sip)
19068c2ecf20Sopenharmony_ci{
19078c2ecf20Sopenharmony_ci	enum mlxsw_sp_fib_entry_type type = MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP;
19088c2ecf20Sopenharmony_ci	struct mlxsw_sp_router *router = mlxsw_sp->router;
19098c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_entry *fib_entry;
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!router->nve_decap_config.valid))
19148c2ecf20Sopenharmony_ci		goto out;
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_ci	router->nve_decap_config.valid = false;
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_ci	fib_entry = mlxsw_sp_router_ip2me_fib_entry_find(mlxsw_sp, ul_tb_id,
19198c2ecf20Sopenharmony_ci							 ul_proto, ul_sip,
19208c2ecf20Sopenharmony_ci							 type);
19218c2ecf20Sopenharmony_ci	if (!fib_entry)
19228c2ecf20Sopenharmony_ci		goto out;
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci	fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
19258c2ecf20Sopenharmony_ci	mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
19268c2ecf20Sopenharmony_ciout:
19278c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
19288c2ecf20Sopenharmony_ci}
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_cistatic bool mlxsw_sp_router_nve_is_decap(struct mlxsw_sp *mlxsw_sp,
19318c2ecf20Sopenharmony_ci					 u32 ul_tb_id,
19328c2ecf20Sopenharmony_ci					 enum mlxsw_sp_l3proto ul_proto,
19338c2ecf20Sopenharmony_ci					 const union mlxsw_sp_l3addr *ul_sip)
19348c2ecf20Sopenharmony_ci{
19358c2ecf20Sopenharmony_ci	struct mlxsw_sp_router *router = mlxsw_sp->router;
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_ci	return router->nve_decap_config.valid &&
19388c2ecf20Sopenharmony_ci	       router->nve_decap_config.ul_tb_id == ul_tb_id &&
19398c2ecf20Sopenharmony_ci	       router->nve_decap_config.ul_proto == ul_proto &&
19408c2ecf20Sopenharmony_ci	       !memcmp(&router->nve_decap_config.ul_sip, ul_sip,
19418c2ecf20Sopenharmony_ci		       sizeof(*ul_sip));
19428c2ecf20Sopenharmony_ci}
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_cistruct mlxsw_sp_neigh_key {
19458c2ecf20Sopenharmony_ci	struct neighbour *n;
19468c2ecf20Sopenharmony_ci};
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_cistruct mlxsw_sp_neigh_entry {
19498c2ecf20Sopenharmony_ci	struct list_head rif_list_node;
19508c2ecf20Sopenharmony_ci	struct rhash_head ht_node;
19518c2ecf20Sopenharmony_ci	struct mlxsw_sp_neigh_key key;
19528c2ecf20Sopenharmony_ci	u16 rif;
19538c2ecf20Sopenharmony_ci	bool connected;
19548c2ecf20Sopenharmony_ci	unsigned char ha[ETH_ALEN];
19558c2ecf20Sopenharmony_ci	struct list_head nexthop_list; /* list of nexthops using
19568c2ecf20Sopenharmony_ci					* this neigh entry
19578c2ecf20Sopenharmony_ci					*/
19588c2ecf20Sopenharmony_ci	struct list_head nexthop_neighs_list_node;
19598c2ecf20Sopenharmony_ci	unsigned int counter_index;
19608c2ecf20Sopenharmony_ci	bool counter_valid;
19618c2ecf20Sopenharmony_ci};
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
19648c2ecf20Sopenharmony_ci	.key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
19658c2ecf20Sopenharmony_ci	.head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
19668c2ecf20Sopenharmony_ci	.key_len = sizeof(struct mlxsw_sp_neigh_key),
19678c2ecf20Sopenharmony_ci};
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_cistruct mlxsw_sp_neigh_entry *
19708c2ecf20Sopenharmony_cimlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif,
19718c2ecf20Sopenharmony_ci			struct mlxsw_sp_neigh_entry *neigh_entry)
19728c2ecf20Sopenharmony_ci{
19738c2ecf20Sopenharmony_ci	if (!neigh_entry) {
19748c2ecf20Sopenharmony_ci		if (list_empty(&rif->neigh_list))
19758c2ecf20Sopenharmony_ci			return NULL;
19768c2ecf20Sopenharmony_ci		else
19778c2ecf20Sopenharmony_ci			return list_first_entry(&rif->neigh_list,
19788c2ecf20Sopenharmony_ci						typeof(*neigh_entry),
19798c2ecf20Sopenharmony_ci						rif_list_node);
19808c2ecf20Sopenharmony_ci	}
19818c2ecf20Sopenharmony_ci	if (list_is_last(&neigh_entry->rif_list_node, &rif->neigh_list))
19828c2ecf20Sopenharmony_ci		return NULL;
19838c2ecf20Sopenharmony_ci	return list_next_entry(neigh_entry, rif_list_node);
19848c2ecf20Sopenharmony_ci}
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ciint mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry)
19878c2ecf20Sopenharmony_ci{
19888c2ecf20Sopenharmony_ci	return neigh_entry->key.n->tbl->family;
19898c2ecf20Sopenharmony_ci}
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_ciunsigned char *
19928c2ecf20Sopenharmony_cimlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry)
19938c2ecf20Sopenharmony_ci{
19948c2ecf20Sopenharmony_ci	return neigh_entry->ha;
19958c2ecf20Sopenharmony_ci}
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_ciu32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
19988c2ecf20Sopenharmony_ci{
19998c2ecf20Sopenharmony_ci	struct neighbour *n;
20008c2ecf20Sopenharmony_ci
20018c2ecf20Sopenharmony_ci	n = neigh_entry->key.n;
20028c2ecf20Sopenharmony_ci	return ntohl(*((__be32 *) n->primary_key));
20038c2ecf20Sopenharmony_ci}
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_cistruct in6_addr *
20068c2ecf20Sopenharmony_cimlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
20078c2ecf20Sopenharmony_ci{
20088c2ecf20Sopenharmony_ci	struct neighbour *n;
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci	n = neigh_entry->key.n;
20118c2ecf20Sopenharmony_ci	return (struct in6_addr *) &n->primary_key;
20128c2ecf20Sopenharmony_ci}
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ciint mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp,
20158c2ecf20Sopenharmony_ci			       struct mlxsw_sp_neigh_entry *neigh_entry,
20168c2ecf20Sopenharmony_ci			       u64 *p_counter)
20178c2ecf20Sopenharmony_ci{
20188c2ecf20Sopenharmony_ci	if (!neigh_entry->counter_valid)
20198c2ecf20Sopenharmony_ci		return -EINVAL;
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci	return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index,
20228c2ecf20Sopenharmony_ci					 p_counter, NULL);
20238c2ecf20Sopenharmony_ci}
20248c2ecf20Sopenharmony_ci
20258c2ecf20Sopenharmony_cistatic struct mlxsw_sp_neigh_entry *
20268c2ecf20Sopenharmony_cimlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
20278c2ecf20Sopenharmony_ci			   u16 rif)
20288c2ecf20Sopenharmony_ci{
20298c2ecf20Sopenharmony_ci	struct mlxsw_sp_neigh_entry *neigh_entry;
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_ci	neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
20328c2ecf20Sopenharmony_ci	if (!neigh_entry)
20338c2ecf20Sopenharmony_ci		return NULL;
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci	neigh_entry->key.n = n;
20368c2ecf20Sopenharmony_ci	neigh_entry->rif = rif;
20378c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&neigh_entry->nexthop_list);
20388c2ecf20Sopenharmony_ci
20398c2ecf20Sopenharmony_ci	return neigh_entry;
20408c2ecf20Sopenharmony_ci}
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_cistatic void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
20438c2ecf20Sopenharmony_ci{
20448c2ecf20Sopenharmony_ci	kfree(neigh_entry);
20458c2ecf20Sopenharmony_ci}
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_cistatic int
20488c2ecf20Sopenharmony_cimlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
20498c2ecf20Sopenharmony_ci			    struct mlxsw_sp_neigh_entry *neigh_entry)
20508c2ecf20Sopenharmony_ci{
20518c2ecf20Sopenharmony_ci	return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
20528c2ecf20Sopenharmony_ci				      &neigh_entry->ht_node,
20538c2ecf20Sopenharmony_ci				      mlxsw_sp_neigh_ht_params);
20548c2ecf20Sopenharmony_ci}
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_cistatic void
20578c2ecf20Sopenharmony_cimlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
20588c2ecf20Sopenharmony_ci			    struct mlxsw_sp_neigh_entry *neigh_entry)
20598c2ecf20Sopenharmony_ci{
20608c2ecf20Sopenharmony_ci	rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
20618c2ecf20Sopenharmony_ci			       &neigh_entry->ht_node,
20628c2ecf20Sopenharmony_ci			       mlxsw_sp_neigh_ht_params);
20638c2ecf20Sopenharmony_ci}
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_cistatic bool
20668c2ecf20Sopenharmony_cimlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp,
20678c2ecf20Sopenharmony_ci				    struct mlxsw_sp_neigh_entry *neigh_entry)
20688c2ecf20Sopenharmony_ci{
20698c2ecf20Sopenharmony_ci	struct devlink *devlink;
20708c2ecf20Sopenharmony_ci	const char *table_name;
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	switch (mlxsw_sp_neigh_entry_type(neigh_entry)) {
20738c2ecf20Sopenharmony_ci	case AF_INET:
20748c2ecf20Sopenharmony_ci		table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4;
20758c2ecf20Sopenharmony_ci		break;
20768c2ecf20Sopenharmony_ci	case AF_INET6:
20778c2ecf20Sopenharmony_ci		table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6;
20788c2ecf20Sopenharmony_ci		break;
20798c2ecf20Sopenharmony_ci	default:
20808c2ecf20Sopenharmony_ci		WARN_ON(1);
20818c2ecf20Sopenharmony_ci		return false;
20828c2ecf20Sopenharmony_ci	}
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_ci	devlink = priv_to_devlink(mlxsw_sp->core);
20858c2ecf20Sopenharmony_ci	return devlink_dpipe_table_counter_enabled(devlink, table_name);
20868c2ecf20Sopenharmony_ci}
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_cistatic void
20898c2ecf20Sopenharmony_cimlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp,
20908c2ecf20Sopenharmony_ci			     struct mlxsw_sp_neigh_entry *neigh_entry)
20918c2ecf20Sopenharmony_ci{
20928c2ecf20Sopenharmony_ci	if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry))
20938c2ecf20Sopenharmony_ci		return;
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_ci	if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index))
20968c2ecf20Sopenharmony_ci		return;
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci	neigh_entry->counter_valid = true;
20998c2ecf20Sopenharmony_ci}
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_cistatic void
21028c2ecf20Sopenharmony_cimlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp,
21038c2ecf20Sopenharmony_ci			    struct mlxsw_sp_neigh_entry *neigh_entry)
21048c2ecf20Sopenharmony_ci{
21058c2ecf20Sopenharmony_ci	if (!neigh_entry->counter_valid)
21068c2ecf20Sopenharmony_ci		return;
21078c2ecf20Sopenharmony_ci	mlxsw_sp_flow_counter_free(mlxsw_sp,
21088c2ecf20Sopenharmony_ci				   neigh_entry->counter_index);
21098c2ecf20Sopenharmony_ci	neigh_entry->counter_valid = false;
21108c2ecf20Sopenharmony_ci}
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_cistatic struct mlxsw_sp_neigh_entry *
21138c2ecf20Sopenharmony_cimlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
21148c2ecf20Sopenharmony_ci{
21158c2ecf20Sopenharmony_ci	struct mlxsw_sp_neigh_entry *neigh_entry;
21168c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
21178c2ecf20Sopenharmony_ci	int err;
21188c2ecf20Sopenharmony_ci
21198c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
21208c2ecf20Sopenharmony_ci	if (!rif)
21218c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_ci	neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
21248c2ecf20Sopenharmony_ci	if (!neigh_entry)
21258c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
21288c2ecf20Sopenharmony_ci	if (err)
21298c2ecf20Sopenharmony_ci		goto err_neigh_entry_insert;
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci	mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
21328c2ecf20Sopenharmony_ci	list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci	return neigh_entry;
21358c2ecf20Sopenharmony_ci
21368c2ecf20Sopenharmony_cierr_neigh_entry_insert:
21378c2ecf20Sopenharmony_ci	mlxsw_sp_neigh_entry_free(neigh_entry);
21388c2ecf20Sopenharmony_ci	return ERR_PTR(err);
21398c2ecf20Sopenharmony_ci}
21408c2ecf20Sopenharmony_ci
21418c2ecf20Sopenharmony_cistatic void
21428c2ecf20Sopenharmony_cimlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
21438c2ecf20Sopenharmony_ci			     struct mlxsw_sp_neigh_entry *neigh_entry)
21448c2ecf20Sopenharmony_ci{
21458c2ecf20Sopenharmony_ci	list_del(&neigh_entry->rif_list_node);
21468c2ecf20Sopenharmony_ci	mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
21478c2ecf20Sopenharmony_ci	mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
21488c2ecf20Sopenharmony_ci	mlxsw_sp_neigh_entry_free(neigh_entry);
21498c2ecf20Sopenharmony_ci}
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_cistatic struct mlxsw_sp_neigh_entry *
21528c2ecf20Sopenharmony_cimlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
21538c2ecf20Sopenharmony_ci{
21548c2ecf20Sopenharmony_ci	struct mlxsw_sp_neigh_key key;
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci	key.n = n;
21578c2ecf20Sopenharmony_ci	return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
21588c2ecf20Sopenharmony_ci				      &key, mlxsw_sp_neigh_ht_params);
21598c2ecf20Sopenharmony_ci}
21608c2ecf20Sopenharmony_ci
21618c2ecf20Sopenharmony_cistatic void
21628c2ecf20Sopenharmony_cimlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
21638c2ecf20Sopenharmony_ci{
21648c2ecf20Sopenharmony_ci	unsigned long interval;
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
21678c2ecf20Sopenharmony_ci	interval = min_t(unsigned long,
21688c2ecf20Sopenharmony_ci			 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
21698c2ecf20Sopenharmony_ci			 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
21708c2ecf20Sopenharmony_ci#else
21718c2ecf20Sopenharmony_ci	interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
21728c2ecf20Sopenharmony_ci#endif
21738c2ecf20Sopenharmony_ci	mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
21748c2ecf20Sopenharmony_ci}
21758c2ecf20Sopenharmony_ci
21768c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
21778c2ecf20Sopenharmony_ci						   char *rauhtd_pl,
21788c2ecf20Sopenharmony_ci						   int ent_index)
21798c2ecf20Sopenharmony_ci{
21808c2ecf20Sopenharmony_ci	u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
21818c2ecf20Sopenharmony_ci	struct net_device *dev;
21828c2ecf20Sopenharmony_ci	struct neighbour *n;
21838c2ecf20Sopenharmony_ci	__be32 dipn;
21848c2ecf20Sopenharmony_ci	u32 dip;
21858c2ecf20Sopenharmony_ci	u16 rif;
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_ci	mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
21888c2ecf20Sopenharmony_ci
21898c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(rif >= max_rifs))
21908c2ecf20Sopenharmony_ci		return;
21918c2ecf20Sopenharmony_ci	if (!mlxsw_sp->router->rifs[rif]) {
21928c2ecf20Sopenharmony_ci		dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
21938c2ecf20Sopenharmony_ci		return;
21948c2ecf20Sopenharmony_ci	}
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci	dipn = htonl(dip);
21978c2ecf20Sopenharmony_ci	dev = mlxsw_sp->router->rifs[rif]->dev;
21988c2ecf20Sopenharmony_ci	n = neigh_lookup(&arp_tbl, &dipn, dev);
21998c2ecf20Sopenharmony_ci	if (!n)
22008c2ecf20Sopenharmony_ci		return;
22018c2ecf20Sopenharmony_ci
22028c2ecf20Sopenharmony_ci	netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
22038c2ecf20Sopenharmony_ci	neigh_event_send(n, NULL);
22048c2ecf20Sopenharmony_ci	neigh_release(n);
22058c2ecf20Sopenharmony_ci}
22068c2ecf20Sopenharmony_ci
22078c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
22088c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
22098c2ecf20Sopenharmony_ci						   char *rauhtd_pl,
22108c2ecf20Sopenharmony_ci						   int rec_index)
22118c2ecf20Sopenharmony_ci{
22128c2ecf20Sopenharmony_ci	struct net_device *dev;
22138c2ecf20Sopenharmony_ci	struct neighbour *n;
22148c2ecf20Sopenharmony_ci	struct in6_addr dip;
22158c2ecf20Sopenharmony_ci	u16 rif;
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_ci	mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
22188c2ecf20Sopenharmony_ci					 (char *) &dip);
22198c2ecf20Sopenharmony_ci
22208c2ecf20Sopenharmony_ci	if (!mlxsw_sp->router->rifs[rif]) {
22218c2ecf20Sopenharmony_ci		dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
22228c2ecf20Sopenharmony_ci		return;
22238c2ecf20Sopenharmony_ci	}
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_ci	dev = mlxsw_sp->router->rifs[rif]->dev;
22268c2ecf20Sopenharmony_ci	n = neigh_lookup(&nd_tbl, &dip, dev);
22278c2ecf20Sopenharmony_ci	if (!n)
22288c2ecf20Sopenharmony_ci		return;
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci	netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
22318c2ecf20Sopenharmony_ci	neigh_event_send(n, NULL);
22328c2ecf20Sopenharmony_ci	neigh_release(n);
22338c2ecf20Sopenharmony_ci}
22348c2ecf20Sopenharmony_ci#else
22358c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
22368c2ecf20Sopenharmony_ci						   char *rauhtd_pl,
22378c2ecf20Sopenharmony_ci						   int rec_index)
22388c2ecf20Sopenharmony_ci{
22398c2ecf20Sopenharmony_ci}
22408c2ecf20Sopenharmony_ci#endif
22418c2ecf20Sopenharmony_ci
22428c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
22438c2ecf20Sopenharmony_ci						   char *rauhtd_pl,
22448c2ecf20Sopenharmony_ci						   int rec_index)
22458c2ecf20Sopenharmony_ci{
22468c2ecf20Sopenharmony_ci	u8 num_entries;
22478c2ecf20Sopenharmony_ci	int i;
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_ci	num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
22508c2ecf20Sopenharmony_ci								rec_index);
22518c2ecf20Sopenharmony_ci	/* Hardware starts counting at 0, so add 1. */
22528c2ecf20Sopenharmony_ci	num_entries++;
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_ci	/* Each record consists of several neighbour entries. */
22558c2ecf20Sopenharmony_ci	for (i = 0; i < num_entries; i++) {
22568c2ecf20Sopenharmony_ci		int ent_index;
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci		ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
22598c2ecf20Sopenharmony_ci		mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
22608c2ecf20Sopenharmony_ci						       ent_index);
22618c2ecf20Sopenharmony_ci	}
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_ci}
22648c2ecf20Sopenharmony_ci
22658c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
22668c2ecf20Sopenharmony_ci						   char *rauhtd_pl,
22678c2ecf20Sopenharmony_ci						   int rec_index)
22688c2ecf20Sopenharmony_ci{
22698c2ecf20Sopenharmony_ci	/* One record contains one entry. */
22708c2ecf20Sopenharmony_ci	mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
22718c2ecf20Sopenharmony_ci					       rec_index);
22728c2ecf20Sopenharmony_ci}
22738c2ecf20Sopenharmony_ci
22748c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
22758c2ecf20Sopenharmony_ci					      char *rauhtd_pl, int rec_index)
22768c2ecf20Sopenharmony_ci{
22778c2ecf20Sopenharmony_ci	switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
22788c2ecf20Sopenharmony_ci	case MLXSW_REG_RAUHTD_TYPE_IPV4:
22798c2ecf20Sopenharmony_ci		mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
22808c2ecf20Sopenharmony_ci						       rec_index);
22818c2ecf20Sopenharmony_ci		break;
22828c2ecf20Sopenharmony_ci	case MLXSW_REG_RAUHTD_TYPE_IPV6:
22838c2ecf20Sopenharmony_ci		mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
22848c2ecf20Sopenharmony_ci						       rec_index);
22858c2ecf20Sopenharmony_ci		break;
22868c2ecf20Sopenharmony_ci	}
22878c2ecf20Sopenharmony_ci}
22888c2ecf20Sopenharmony_ci
22898c2ecf20Sopenharmony_cistatic bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
22908c2ecf20Sopenharmony_ci{
22918c2ecf20Sopenharmony_ci	u8 num_rec, last_rec_index, num_entries;
22928c2ecf20Sopenharmony_ci
22938c2ecf20Sopenharmony_ci	num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
22948c2ecf20Sopenharmony_ci	last_rec_index = num_rec - 1;
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ci	if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
22978c2ecf20Sopenharmony_ci		return false;
22988c2ecf20Sopenharmony_ci	if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
22998c2ecf20Sopenharmony_ci	    MLXSW_REG_RAUHTD_TYPE_IPV6)
23008c2ecf20Sopenharmony_ci		return true;
23018c2ecf20Sopenharmony_ci
23028c2ecf20Sopenharmony_ci	num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
23038c2ecf20Sopenharmony_ci								last_rec_index);
23048c2ecf20Sopenharmony_ci	if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
23058c2ecf20Sopenharmony_ci		return true;
23068c2ecf20Sopenharmony_ci	return false;
23078c2ecf20Sopenharmony_ci}
23088c2ecf20Sopenharmony_ci
23098c2ecf20Sopenharmony_cistatic int
23108c2ecf20Sopenharmony_ci__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
23118c2ecf20Sopenharmony_ci				       char *rauhtd_pl,
23128c2ecf20Sopenharmony_ci				       enum mlxsw_reg_rauhtd_type type)
23138c2ecf20Sopenharmony_ci{
23148c2ecf20Sopenharmony_ci	int i, num_rec;
23158c2ecf20Sopenharmony_ci	int err;
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci	/* Ensure the RIF we read from the device does not change mid-dump. */
23188c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
23198c2ecf20Sopenharmony_ci	do {
23208c2ecf20Sopenharmony_ci		mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
23218c2ecf20Sopenharmony_ci		err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
23228c2ecf20Sopenharmony_ci				      rauhtd_pl);
23238c2ecf20Sopenharmony_ci		if (err) {
23248c2ecf20Sopenharmony_ci			dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour table\n");
23258c2ecf20Sopenharmony_ci			break;
23268c2ecf20Sopenharmony_ci		}
23278c2ecf20Sopenharmony_ci		num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
23288c2ecf20Sopenharmony_ci		for (i = 0; i < num_rec; i++)
23298c2ecf20Sopenharmony_ci			mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
23308c2ecf20Sopenharmony_ci							  i);
23318c2ecf20Sopenharmony_ci	} while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
23328c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
23338c2ecf20Sopenharmony_ci
23348c2ecf20Sopenharmony_ci	return err;
23358c2ecf20Sopenharmony_ci}
23368c2ecf20Sopenharmony_ci
23378c2ecf20Sopenharmony_cistatic int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
23388c2ecf20Sopenharmony_ci{
23398c2ecf20Sopenharmony_ci	enum mlxsw_reg_rauhtd_type type;
23408c2ecf20Sopenharmony_ci	char *rauhtd_pl;
23418c2ecf20Sopenharmony_ci	int err;
23428c2ecf20Sopenharmony_ci
23438c2ecf20Sopenharmony_ci	rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
23448c2ecf20Sopenharmony_ci	if (!rauhtd_pl)
23458c2ecf20Sopenharmony_ci		return -ENOMEM;
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ci	type = MLXSW_REG_RAUHTD_TYPE_IPV4;
23488c2ecf20Sopenharmony_ci	err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
23498c2ecf20Sopenharmony_ci	if (err)
23508c2ecf20Sopenharmony_ci		goto out;
23518c2ecf20Sopenharmony_ci
23528c2ecf20Sopenharmony_ci	type = MLXSW_REG_RAUHTD_TYPE_IPV6;
23538c2ecf20Sopenharmony_ci	err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
23548c2ecf20Sopenharmony_ciout:
23558c2ecf20Sopenharmony_ci	kfree(rauhtd_pl);
23568c2ecf20Sopenharmony_ci	return err;
23578c2ecf20Sopenharmony_ci}
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
23608c2ecf20Sopenharmony_ci{
23618c2ecf20Sopenharmony_ci	struct mlxsw_sp_neigh_entry *neigh_entry;
23628c2ecf20Sopenharmony_ci
23638c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
23648c2ecf20Sopenharmony_ci	list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
23658c2ecf20Sopenharmony_ci			    nexthop_neighs_list_node)
23668c2ecf20Sopenharmony_ci		/* If this neigh have nexthops, make the kernel think this neigh
23678c2ecf20Sopenharmony_ci		 * is active regardless of the traffic.
23688c2ecf20Sopenharmony_ci		 */
23698c2ecf20Sopenharmony_ci		neigh_event_send(neigh_entry->key.n, NULL);
23708c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
23718c2ecf20Sopenharmony_ci}
23728c2ecf20Sopenharmony_ci
23738c2ecf20Sopenharmony_cistatic void
23748c2ecf20Sopenharmony_cimlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
23758c2ecf20Sopenharmony_ci{
23768c2ecf20Sopenharmony_ci	unsigned long interval = mlxsw_sp->router->neighs_update.interval;
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
23798c2ecf20Sopenharmony_ci			       msecs_to_jiffies(interval));
23808c2ecf20Sopenharmony_ci}
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
23838c2ecf20Sopenharmony_ci{
23848c2ecf20Sopenharmony_ci	struct mlxsw_sp_router *router;
23858c2ecf20Sopenharmony_ci	int err;
23868c2ecf20Sopenharmony_ci
23878c2ecf20Sopenharmony_ci	router = container_of(work, struct mlxsw_sp_router,
23888c2ecf20Sopenharmony_ci			      neighs_update.dw.work);
23898c2ecf20Sopenharmony_ci	err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
23908c2ecf20Sopenharmony_ci	if (err)
23918c2ecf20Sopenharmony_ci		dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_ci	mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
23948c2ecf20Sopenharmony_ci
23958c2ecf20Sopenharmony_ci	mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
23968c2ecf20Sopenharmony_ci}
23978c2ecf20Sopenharmony_ci
23988c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
23998c2ecf20Sopenharmony_ci{
24008c2ecf20Sopenharmony_ci	struct mlxsw_sp_neigh_entry *neigh_entry;
24018c2ecf20Sopenharmony_ci	struct mlxsw_sp_router *router;
24028c2ecf20Sopenharmony_ci
24038c2ecf20Sopenharmony_ci	router = container_of(work, struct mlxsw_sp_router,
24048c2ecf20Sopenharmony_ci			      nexthop_probe_dw.work);
24058c2ecf20Sopenharmony_ci	/* Iterate over nexthop neighbours, find those who are unresolved and
24068c2ecf20Sopenharmony_ci	 * send arp on them. This solves the chicken-egg problem when
24078c2ecf20Sopenharmony_ci	 * the nexthop wouldn't get offloaded until the neighbor is resolved
24088c2ecf20Sopenharmony_ci	 * but it wouldn't get resolved ever in case traffic is flowing in HW
24098c2ecf20Sopenharmony_ci	 * using different nexthop.
24108c2ecf20Sopenharmony_ci	 */
24118c2ecf20Sopenharmony_ci	mutex_lock(&router->lock);
24128c2ecf20Sopenharmony_ci	list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
24138c2ecf20Sopenharmony_ci			    nexthop_neighs_list_node)
24148c2ecf20Sopenharmony_ci		if (!neigh_entry->connected)
24158c2ecf20Sopenharmony_ci			neigh_event_send(neigh_entry->key.n, NULL);
24168c2ecf20Sopenharmony_ci	mutex_unlock(&router->lock);
24178c2ecf20Sopenharmony_ci
24188c2ecf20Sopenharmony_ci	mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
24198c2ecf20Sopenharmony_ci			       MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
24208c2ecf20Sopenharmony_ci}
24218c2ecf20Sopenharmony_ci
24228c2ecf20Sopenharmony_cistatic void
24238c2ecf20Sopenharmony_cimlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
24248c2ecf20Sopenharmony_ci			      struct mlxsw_sp_neigh_entry *neigh_entry,
24258c2ecf20Sopenharmony_ci			      bool removing, bool dead);
24268c2ecf20Sopenharmony_ci
24278c2ecf20Sopenharmony_cistatic enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
24288c2ecf20Sopenharmony_ci{
24298c2ecf20Sopenharmony_ci	return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
24308c2ecf20Sopenharmony_ci			MLXSW_REG_RAUHT_OP_WRITE_DELETE;
24318c2ecf20Sopenharmony_ci}
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_cistatic int
24348c2ecf20Sopenharmony_cimlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
24358c2ecf20Sopenharmony_ci				struct mlxsw_sp_neigh_entry *neigh_entry,
24368c2ecf20Sopenharmony_ci				enum mlxsw_reg_rauht_op op)
24378c2ecf20Sopenharmony_ci{
24388c2ecf20Sopenharmony_ci	struct neighbour *n = neigh_entry->key.n;
24398c2ecf20Sopenharmony_ci	u32 dip = ntohl(*((__be32 *) n->primary_key));
24408c2ecf20Sopenharmony_ci	char rauht_pl[MLXSW_REG_RAUHT_LEN];
24418c2ecf20Sopenharmony_ci
24428c2ecf20Sopenharmony_ci	mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
24438c2ecf20Sopenharmony_ci			      dip);
24448c2ecf20Sopenharmony_ci	if (neigh_entry->counter_valid)
24458c2ecf20Sopenharmony_ci		mlxsw_reg_rauht_pack_counter(rauht_pl,
24468c2ecf20Sopenharmony_ci					     neigh_entry->counter_index);
24478c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
24488c2ecf20Sopenharmony_ci}
24498c2ecf20Sopenharmony_ci
24508c2ecf20Sopenharmony_cistatic int
24518c2ecf20Sopenharmony_cimlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
24528c2ecf20Sopenharmony_ci				struct mlxsw_sp_neigh_entry *neigh_entry,
24538c2ecf20Sopenharmony_ci				enum mlxsw_reg_rauht_op op)
24548c2ecf20Sopenharmony_ci{
24558c2ecf20Sopenharmony_ci	struct neighbour *n = neigh_entry->key.n;
24568c2ecf20Sopenharmony_ci	char rauht_pl[MLXSW_REG_RAUHT_LEN];
24578c2ecf20Sopenharmony_ci	const char *dip = n->primary_key;
24588c2ecf20Sopenharmony_ci
24598c2ecf20Sopenharmony_ci	mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
24608c2ecf20Sopenharmony_ci			      dip);
24618c2ecf20Sopenharmony_ci	if (neigh_entry->counter_valid)
24628c2ecf20Sopenharmony_ci		mlxsw_reg_rauht_pack_counter(rauht_pl,
24638c2ecf20Sopenharmony_ci					     neigh_entry->counter_index);
24648c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
24658c2ecf20Sopenharmony_ci}
24668c2ecf20Sopenharmony_ci
24678c2ecf20Sopenharmony_cibool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry)
24688c2ecf20Sopenharmony_ci{
24698c2ecf20Sopenharmony_ci	struct neighbour *n = neigh_entry->key.n;
24708c2ecf20Sopenharmony_ci
24718c2ecf20Sopenharmony_ci	/* Packets with a link-local destination address are trapped
24728c2ecf20Sopenharmony_ci	 * after LPM lookup and never reach the neighbour table, so
24738c2ecf20Sopenharmony_ci	 * there is no need to program such neighbours to the device.
24748c2ecf20Sopenharmony_ci	 */
24758c2ecf20Sopenharmony_ci	if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
24768c2ecf20Sopenharmony_ci	    IPV6_ADDR_LINKLOCAL)
24778c2ecf20Sopenharmony_ci		return true;
24788c2ecf20Sopenharmony_ci	return false;
24798c2ecf20Sopenharmony_ci}
24808c2ecf20Sopenharmony_ci
24818c2ecf20Sopenharmony_cistatic void
24828c2ecf20Sopenharmony_cimlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
24838c2ecf20Sopenharmony_ci			    struct mlxsw_sp_neigh_entry *neigh_entry,
24848c2ecf20Sopenharmony_ci			    bool adding)
24858c2ecf20Sopenharmony_ci{
24868c2ecf20Sopenharmony_ci	enum mlxsw_reg_rauht_op op = mlxsw_sp_rauht_op(adding);
24878c2ecf20Sopenharmony_ci	int err;
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci	if (!adding && !neigh_entry->connected)
24908c2ecf20Sopenharmony_ci		return;
24918c2ecf20Sopenharmony_ci	neigh_entry->connected = adding;
24928c2ecf20Sopenharmony_ci	if (neigh_entry->key.n->tbl->family == AF_INET) {
24938c2ecf20Sopenharmony_ci		err = mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
24948c2ecf20Sopenharmony_ci						      op);
24958c2ecf20Sopenharmony_ci		if (err)
24968c2ecf20Sopenharmony_ci			return;
24978c2ecf20Sopenharmony_ci	} else if (neigh_entry->key.n->tbl->family == AF_INET6) {
24988c2ecf20Sopenharmony_ci		if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry))
24998c2ecf20Sopenharmony_ci			return;
25008c2ecf20Sopenharmony_ci		err = mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
25018c2ecf20Sopenharmony_ci						      op);
25028c2ecf20Sopenharmony_ci		if (err)
25038c2ecf20Sopenharmony_ci			return;
25048c2ecf20Sopenharmony_ci	} else {
25058c2ecf20Sopenharmony_ci		WARN_ON_ONCE(1);
25068c2ecf20Sopenharmony_ci		return;
25078c2ecf20Sopenharmony_ci	}
25088c2ecf20Sopenharmony_ci
25098c2ecf20Sopenharmony_ci	if (adding)
25108c2ecf20Sopenharmony_ci		neigh_entry->key.n->flags |= NTF_OFFLOADED;
25118c2ecf20Sopenharmony_ci	else
25128c2ecf20Sopenharmony_ci		neigh_entry->key.n->flags &= ~NTF_OFFLOADED;
25138c2ecf20Sopenharmony_ci}
25148c2ecf20Sopenharmony_ci
25158c2ecf20Sopenharmony_civoid
25168c2ecf20Sopenharmony_cimlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp,
25178c2ecf20Sopenharmony_ci				    struct mlxsw_sp_neigh_entry *neigh_entry,
25188c2ecf20Sopenharmony_ci				    bool adding)
25198c2ecf20Sopenharmony_ci{
25208c2ecf20Sopenharmony_ci	if (adding)
25218c2ecf20Sopenharmony_ci		mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
25228c2ecf20Sopenharmony_ci	else
25238c2ecf20Sopenharmony_ci		mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
25248c2ecf20Sopenharmony_ci	mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true);
25258c2ecf20Sopenharmony_ci}
25268c2ecf20Sopenharmony_ci
25278c2ecf20Sopenharmony_cistruct mlxsw_sp_netevent_work {
25288c2ecf20Sopenharmony_ci	struct work_struct work;
25298c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
25308c2ecf20Sopenharmony_ci	struct neighbour *n;
25318c2ecf20Sopenharmony_ci};
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
25348c2ecf20Sopenharmony_ci{
25358c2ecf20Sopenharmony_ci	struct mlxsw_sp_netevent_work *net_work =
25368c2ecf20Sopenharmony_ci		container_of(work, struct mlxsw_sp_netevent_work, work);
25378c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = net_work->mlxsw_sp;
25388c2ecf20Sopenharmony_ci	struct mlxsw_sp_neigh_entry *neigh_entry;
25398c2ecf20Sopenharmony_ci	struct neighbour *n = net_work->n;
25408c2ecf20Sopenharmony_ci	unsigned char ha[ETH_ALEN];
25418c2ecf20Sopenharmony_ci	bool entry_connected;
25428c2ecf20Sopenharmony_ci	u8 nud_state, dead;
25438c2ecf20Sopenharmony_ci
25448c2ecf20Sopenharmony_ci	/* If these parameters are changed after we release the lock,
25458c2ecf20Sopenharmony_ci	 * then we are guaranteed to receive another event letting us
25468c2ecf20Sopenharmony_ci	 * know about it.
25478c2ecf20Sopenharmony_ci	 */
25488c2ecf20Sopenharmony_ci	read_lock_bh(&n->lock);
25498c2ecf20Sopenharmony_ci	memcpy(ha, n->ha, ETH_ALEN);
25508c2ecf20Sopenharmony_ci	nud_state = n->nud_state;
25518c2ecf20Sopenharmony_ci	dead = n->dead;
25528c2ecf20Sopenharmony_ci	read_unlock_bh(&n->lock);
25538c2ecf20Sopenharmony_ci
25548c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
25558c2ecf20Sopenharmony_ci	mlxsw_sp_span_respin(mlxsw_sp);
25568c2ecf20Sopenharmony_ci
25578c2ecf20Sopenharmony_ci	entry_connected = nud_state & NUD_VALID && !dead;
25588c2ecf20Sopenharmony_ci	neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
25598c2ecf20Sopenharmony_ci	if (!entry_connected && !neigh_entry)
25608c2ecf20Sopenharmony_ci		goto out;
25618c2ecf20Sopenharmony_ci	if (!neigh_entry) {
25628c2ecf20Sopenharmony_ci		neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
25638c2ecf20Sopenharmony_ci		if (IS_ERR(neigh_entry))
25648c2ecf20Sopenharmony_ci			goto out;
25658c2ecf20Sopenharmony_ci	}
25668c2ecf20Sopenharmony_ci
25678c2ecf20Sopenharmony_ci	memcpy(neigh_entry->ha, ha, ETH_ALEN);
25688c2ecf20Sopenharmony_ci	mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
25698c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected,
25708c2ecf20Sopenharmony_ci				      dead);
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_ci	if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
25738c2ecf20Sopenharmony_ci		mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
25748c2ecf20Sopenharmony_ci
25758c2ecf20Sopenharmony_ciout:
25768c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
25778c2ecf20Sopenharmony_ci	neigh_release(n);
25788c2ecf20Sopenharmony_ci	kfree(net_work);
25798c2ecf20Sopenharmony_ci}
25808c2ecf20Sopenharmony_ci
25818c2ecf20Sopenharmony_cistatic int mlxsw_sp_mp_hash_init(struct mlxsw_sp *mlxsw_sp);
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_mp_hash_event_work(struct work_struct *work)
25848c2ecf20Sopenharmony_ci{
25858c2ecf20Sopenharmony_ci	struct mlxsw_sp_netevent_work *net_work =
25868c2ecf20Sopenharmony_ci		container_of(work, struct mlxsw_sp_netevent_work, work);
25878c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = net_work->mlxsw_sp;
25888c2ecf20Sopenharmony_ci
25898c2ecf20Sopenharmony_ci	mlxsw_sp_mp_hash_init(mlxsw_sp);
25908c2ecf20Sopenharmony_ci	kfree(net_work);
25918c2ecf20Sopenharmony_ci}
25928c2ecf20Sopenharmony_ci
25938c2ecf20Sopenharmony_cistatic int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp);
25948c2ecf20Sopenharmony_ci
25958c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_update_priority_work(struct work_struct *work)
25968c2ecf20Sopenharmony_ci{
25978c2ecf20Sopenharmony_ci	struct mlxsw_sp_netevent_work *net_work =
25988c2ecf20Sopenharmony_ci		container_of(work, struct mlxsw_sp_netevent_work, work);
25998c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = net_work->mlxsw_sp;
26008c2ecf20Sopenharmony_ci
26018c2ecf20Sopenharmony_ci	__mlxsw_sp_router_init(mlxsw_sp);
26028c2ecf20Sopenharmony_ci	kfree(net_work);
26038c2ecf20Sopenharmony_ci}
26048c2ecf20Sopenharmony_ci
26058c2ecf20Sopenharmony_cistatic int mlxsw_sp_router_schedule_work(struct net *net,
26068c2ecf20Sopenharmony_ci					 struct notifier_block *nb,
26078c2ecf20Sopenharmony_ci					 void (*cb)(struct work_struct *))
26088c2ecf20Sopenharmony_ci{
26098c2ecf20Sopenharmony_ci	struct mlxsw_sp_netevent_work *net_work;
26108c2ecf20Sopenharmony_ci	struct mlxsw_sp_router *router;
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_ci	router = container_of(nb, struct mlxsw_sp_router, netevent_nb);
26138c2ecf20Sopenharmony_ci	if (!net_eq(net, mlxsw_sp_net(router->mlxsw_sp)))
26148c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
26158c2ecf20Sopenharmony_ci
26168c2ecf20Sopenharmony_ci	net_work = kzalloc(sizeof(*net_work), GFP_ATOMIC);
26178c2ecf20Sopenharmony_ci	if (!net_work)
26188c2ecf20Sopenharmony_ci		return NOTIFY_BAD;
26198c2ecf20Sopenharmony_ci
26208c2ecf20Sopenharmony_ci	INIT_WORK(&net_work->work, cb);
26218c2ecf20Sopenharmony_ci	net_work->mlxsw_sp = router->mlxsw_sp;
26228c2ecf20Sopenharmony_ci	mlxsw_core_schedule_work(&net_work->work);
26238c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
26248c2ecf20Sopenharmony_ci}
26258c2ecf20Sopenharmony_ci
26268c2ecf20Sopenharmony_cistatic int mlxsw_sp_router_netevent_event(struct notifier_block *nb,
26278c2ecf20Sopenharmony_ci					  unsigned long event, void *ptr)
26288c2ecf20Sopenharmony_ci{
26298c2ecf20Sopenharmony_ci	struct mlxsw_sp_netevent_work *net_work;
26308c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
26318c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
26328c2ecf20Sopenharmony_ci	unsigned long interval;
26338c2ecf20Sopenharmony_ci	struct neigh_parms *p;
26348c2ecf20Sopenharmony_ci	struct neighbour *n;
26358c2ecf20Sopenharmony_ci
26368c2ecf20Sopenharmony_ci	switch (event) {
26378c2ecf20Sopenharmony_ci	case NETEVENT_DELAY_PROBE_TIME_UPDATE:
26388c2ecf20Sopenharmony_ci		p = ptr;
26398c2ecf20Sopenharmony_ci
26408c2ecf20Sopenharmony_ci		/* We don't care about changes in the default table. */
26418c2ecf20Sopenharmony_ci		if (!p->dev || (p->tbl->family != AF_INET &&
26428c2ecf20Sopenharmony_ci				p->tbl->family != AF_INET6))
26438c2ecf20Sopenharmony_ci			return NOTIFY_DONE;
26448c2ecf20Sopenharmony_ci
26458c2ecf20Sopenharmony_ci		/* We are in atomic context and can't take RTNL mutex,
26468c2ecf20Sopenharmony_ci		 * so use RCU variant to walk the device chain.
26478c2ecf20Sopenharmony_ci		 */
26488c2ecf20Sopenharmony_ci		mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
26498c2ecf20Sopenharmony_ci		if (!mlxsw_sp_port)
26508c2ecf20Sopenharmony_ci			return NOTIFY_DONE;
26518c2ecf20Sopenharmony_ci
26528c2ecf20Sopenharmony_ci		mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
26538c2ecf20Sopenharmony_ci		interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
26548c2ecf20Sopenharmony_ci		mlxsw_sp->router->neighs_update.interval = interval;
26558c2ecf20Sopenharmony_ci
26568c2ecf20Sopenharmony_ci		mlxsw_sp_port_dev_put(mlxsw_sp_port);
26578c2ecf20Sopenharmony_ci		break;
26588c2ecf20Sopenharmony_ci	case NETEVENT_NEIGH_UPDATE:
26598c2ecf20Sopenharmony_ci		n = ptr;
26608c2ecf20Sopenharmony_ci
26618c2ecf20Sopenharmony_ci		if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
26628c2ecf20Sopenharmony_ci			return NOTIFY_DONE;
26638c2ecf20Sopenharmony_ci
26648c2ecf20Sopenharmony_ci		mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
26658c2ecf20Sopenharmony_ci		if (!mlxsw_sp_port)
26668c2ecf20Sopenharmony_ci			return NOTIFY_DONE;
26678c2ecf20Sopenharmony_ci
26688c2ecf20Sopenharmony_ci		net_work = kzalloc(sizeof(*net_work), GFP_ATOMIC);
26698c2ecf20Sopenharmony_ci		if (!net_work) {
26708c2ecf20Sopenharmony_ci			mlxsw_sp_port_dev_put(mlxsw_sp_port);
26718c2ecf20Sopenharmony_ci			return NOTIFY_BAD;
26728c2ecf20Sopenharmony_ci		}
26738c2ecf20Sopenharmony_ci
26748c2ecf20Sopenharmony_ci		INIT_WORK(&net_work->work, mlxsw_sp_router_neigh_event_work);
26758c2ecf20Sopenharmony_ci		net_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
26768c2ecf20Sopenharmony_ci		net_work->n = n;
26778c2ecf20Sopenharmony_ci
26788c2ecf20Sopenharmony_ci		/* Take a reference to ensure the neighbour won't be
26798c2ecf20Sopenharmony_ci		 * destructed until we drop the reference in delayed
26808c2ecf20Sopenharmony_ci		 * work.
26818c2ecf20Sopenharmony_ci		 */
26828c2ecf20Sopenharmony_ci		neigh_clone(n);
26838c2ecf20Sopenharmony_ci		mlxsw_core_schedule_work(&net_work->work);
26848c2ecf20Sopenharmony_ci		mlxsw_sp_port_dev_put(mlxsw_sp_port);
26858c2ecf20Sopenharmony_ci		break;
26868c2ecf20Sopenharmony_ci	case NETEVENT_IPV4_MPATH_HASH_UPDATE:
26878c2ecf20Sopenharmony_ci	case NETEVENT_IPV6_MPATH_HASH_UPDATE:
26888c2ecf20Sopenharmony_ci		return mlxsw_sp_router_schedule_work(ptr, nb,
26898c2ecf20Sopenharmony_ci				mlxsw_sp_router_mp_hash_event_work);
26908c2ecf20Sopenharmony_ci
26918c2ecf20Sopenharmony_ci	case NETEVENT_IPV4_FWD_UPDATE_PRIORITY_UPDATE:
26928c2ecf20Sopenharmony_ci		return mlxsw_sp_router_schedule_work(ptr, nb,
26938c2ecf20Sopenharmony_ci				mlxsw_sp_router_update_priority_work);
26948c2ecf20Sopenharmony_ci	}
26958c2ecf20Sopenharmony_ci
26968c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
26978c2ecf20Sopenharmony_ci}
26988c2ecf20Sopenharmony_ci
26998c2ecf20Sopenharmony_cistatic int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
27008c2ecf20Sopenharmony_ci{
27018c2ecf20Sopenharmony_ci	int err;
27028c2ecf20Sopenharmony_ci
27038c2ecf20Sopenharmony_ci	err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
27048c2ecf20Sopenharmony_ci			      &mlxsw_sp_neigh_ht_params);
27058c2ecf20Sopenharmony_ci	if (err)
27068c2ecf20Sopenharmony_ci		return err;
27078c2ecf20Sopenharmony_ci
27088c2ecf20Sopenharmony_ci	/* Initialize the polling interval according to the default
27098c2ecf20Sopenharmony_ci	 * table.
27108c2ecf20Sopenharmony_ci	 */
27118c2ecf20Sopenharmony_ci	mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
27128c2ecf20Sopenharmony_ci
27138c2ecf20Sopenharmony_ci	/* Create the delayed works for the activity_update */
27148c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
27158c2ecf20Sopenharmony_ci			  mlxsw_sp_router_neighs_update_work);
27168c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
27178c2ecf20Sopenharmony_ci			  mlxsw_sp_router_probe_unresolved_nexthops);
27188c2ecf20Sopenharmony_ci	mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
27198c2ecf20Sopenharmony_ci	mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
27208c2ecf20Sopenharmony_ci	return 0;
27218c2ecf20Sopenharmony_ci}
27228c2ecf20Sopenharmony_ci
27238c2ecf20Sopenharmony_cistatic void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
27248c2ecf20Sopenharmony_ci{
27258c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
27268c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
27278c2ecf20Sopenharmony_ci	rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
27288c2ecf20Sopenharmony_ci}
27298c2ecf20Sopenharmony_ci
27308c2ecf20Sopenharmony_cistatic void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
27318c2ecf20Sopenharmony_ci					 struct mlxsw_sp_rif *rif)
27328c2ecf20Sopenharmony_ci{
27338c2ecf20Sopenharmony_ci	struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
27348c2ecf20Sopenharmony_ci
27358c2ecf20Sopenharmony_ci	list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
27368c2ecf20Sopenharmony_ci				 rif_list_node) {
27378c2ecf20Sopenharmony_ci		mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
27388c2ecf20Sopenharmony_ci		mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
27398c2ecf20Sopenharmony_ci	}
27408c2ecf20Sopenharmony_ci}
27418c2ecf20Sopenharmony_ci
27428c2ecf20Sopenharmony_cienum mlxsw_sp_nexthop_type {
27438c2ecf20Sopenharmony_ci	MLXSW_SP_NEXTHOP_TYPE_ETH,
27448c2ecf20Sopenharmony_ci	MLXSW_SP_NEXTHOP_TYPE_IPIP,
27458c2ecf20Sopenharmony_ci};
27468c2ecf20Sopenharmony_ci
27478c2ecf20Sopenharmony_cistruct mlxsw_sp_nexthop_key {
27488c2ecf20Sopenharmony_ci	struct fib_nh *fib_nh;
27498c2ecf20Sopenharmony_ci};
27508c2ecf20Sopenharmony_ci
27518c2ecf20Sopenharmony_cistruct mlxsw_sp_nexthop {
27528c2ecf20Sopenharmony_ci	struct list_head neigh_list_node; /* member of neigh entry list */
27538c2ecf20Sopenharmony_ci	struct list_head rif_list_node;
27548c2ecf20Sopenharmony_ci	struct list_head router_list_node;
27558c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
27568c2ecf20Sopenharmony_ci						* this belongs to
27578c2ecf20Sopenharmony_ci						*/
27588c2ecf20Sopenharmony_ci	struct rhash_head ht_node;
27598c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_key key;
27608c2ecf20Sopenharmony_ci	unsigned char gw_addr[sizeof(struct in6_addr)];
27618c2ecf20Sopenharmony_ci	int ifindex;
27628c2ecf20Sopenharmony_ci	int nh_weight;
27638c2ecf20Sopenharmony_ci	int norm_nh_weight;
27648c2ecf20Sopenharmony_ci	int num_adj_entries;
27658c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
27668c2ecf20Sopenharmony_ci	u8 should_offload:1, /* set indicates this neigh is connected and
27678c2ecf20Sopenharmony_ci			      * should be put to KVD linear area of this group.
27688c2ecf20Sopenharmony_ci			      */
27698c2ecf20Sopenharmony_ci	   offloaded:1, /* set in case the neigh is actually put into
27708c2ecf20Sopenharmony_ci			 * KVD linear area of this group.
27718c2ecf20Sopenharmony_ci			 */
27728c2ecf20Sopenharmony_ci	   update:1; /* set indicates that MAC of this neigh should be
27738c2ecf20Sopenharmony_ci		      * updated in HW
27748c2ecf20Sopenharmony_ci		      */
27758c2ecf20Sopenharmony_ci	enum mlxsw_sp_nexthop_type type;
27768c2ecf20Sopenharmony_ci	union {
27778c2ecf20Sopenharmony_ci		struct mlxsw_sp_neigh_entry *neigh_entry;
27788c2ecf20Sopenharmony_ci		struct mlxsw_sp_ipip_entry *ipip_entry;
27798c2ecf20Sopenharmony_ci	};
27808c2ecf20Sopenharmony_ci	unsigned int counter_index;
27818c2ecf20Sopenharmony_ci	bool counter_valid;
27828c2ecf20Sopenharmony_ci};
27838c2ecf20Sopenharmony_ci
27848c2ecf20Sopenharmony_cistruct mlxsw_sp_nexthop_group {
27858c2ecf20Sopenharmony_ci	void *priv;
27868c2ecf20Sopenharmony_ci	struct rhash_head ht_node;
27878c2ecf20Sopenharmony_ci	struct list_head fib_list; /* list of fib entries that use this group */
27888c2ecf20Sopenharmony_ci	struct neigh_table *neigh_tbl;
27898c2ecf20Sopenharmony_ci	u8 adj_index_valid:1,
27908c2ecf20Sopenharmony_ci	   gateway:1; /* routes using the group use a gateway */
27918c2ecf20Sopenharmony_ci	u32 adj_index;
27928c2ecf20Sopenharmony_ci	u16 ecmp_size;
27938c2ecf20Sopenharmony_ci	u16 count;
27948c2ecf20Sopenharmony_ci	int sum_norm_weight;
27958c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop nexthops[0];
27968c2ecf20Sopenharmony_ci#define nh_rif	nexthops[0].rif
27978c2ecf20Sopenharmony_ci};
27988c2ecf20Sopenharmony_ci
27998c2ecf20Sopenharmony_civoid mlxsw_sp_nexthop_counter_alloc(struct mlxsw_sp *mlxsw_sp,
28008c2ecf20Sopenharmony_ci				    struct mlxsw_sp_nexthop *nh)
28018c2ecf20Sopenharmony_ci{
28028c2ecf20Sopenharmony_ci	struct devlink *devlink;
28038c2ecf20Sopenharmony_ci
28048c2ecf20Sopenharmony_ci	devlink = priv_to_devlink(mlxsw_sp->core);
28058c2ecf20Sopenharmony_ci	if (!devlink_dpipe_table_counter_enabled(devlink,
28068c2ecf20Sopenharmony_ci						 MLXSW_SP_DPIPE_TABLE_NAME_ADJ))
28078c2ecf20Sopenharmony_ci		return;
28088c2ecf20Sopenharmony_ci
28098c2ecf20Sopenharmony_ci	if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &nh->counter_index))
28108c2ecf20Sopenharmony_ci		return;
28118c2ecf20Sopenharmony_ci
28128c2ecf20Sopenharmony_ci	nh->counter_valid = true;
28138c2ecf20Sopenharmony_ci}
28148c2ecf20Sopenharmony_ci
28158c2ecf20Sopenharmony_civoid mlxsw_sp_nexthop_counter_free(struct mlxsw_sp *mlxsw_sp,
28168c2ecf20Sopenharmony_ci				   struct mlxsw_sp_nexthop *nh)
28178c2ecf20Sopenharmony_ci{
28188c2ecf20Sopenharmony_ci	if (!nh->counter_valid)
28198c2ecf20Sopenharmony_ci		return;
28208c2ecf20Sopenharmony_ci	mlxsw_sp_flow_counter_free(mlxsw_sp, nh->counter_index);
28218c2ecf20Sopenharmony_ci	nh->counter_valid = false;
28228c2ecf20Sopenharmony_ci}
28238c2ecf20Sopenharmony_ci
28248c2ecf20Sopenharmony_ciint mlxsw_sp_nexthop_counter_get(struct mlxsw_sp *mlxsw_sp,
28258c2ecf20Sopenharmony_ci				 struct mlxsw_sp_nexthop *nh, u64 *p_counter)
28268c2ecf20Sopenharmony_ci{
28278c2ecf20Sopenharmony_ci	if (!nh->counter_valid)
28288c2ecf20Sopenharmony_ci		return -EINVAL;
28298c2ecf20Sopenharmony_ci
28308c2ecf20Sopenharmony_ci	return mlxsw_sp_flow_counter_get(mlxsw_sp, nh->counter_index,
28318c2ecf20Sopenharmony_ci					 p_counter, NULL);
28328c2ecf20Sopenharmony_ci}
28338c2ecf20Sopenharmony_ci
28348c2ecf20Sopenharmony_cistruct mlxsw_sp_nexthop *mlxsw_sp_nexthop_next(struct mlxsw_sp_router *router,
28358c2ecf20Sopenharmony_ci					       struct mlxsw_sp_nexthop *nh)
28368c2ecf20Sopenharmony_ci{
28378c2ecf20Sopenharmony_ci	if (!nh) {
28388c2ecf20Sopenharmony_ci		if (list_empty(&router->nexthop_list))
28398c2ecf20Sopenharmony_ci			return NULL;
28408c2ecf20Sopenharmony_ci		else
28418c2ecf20Sopenharmony_ci			return list_first_entry(&router->nexthop_list,
28428c2ecf20Sopenharmony_ci						typeof(*nh), router_list_node);
28438c2ecf20Sopenharmony_ci	}
28448c2ecf20Sopenharmony_ci	if (list_is_last(&nh->router_list_node, &router->nexthop_list))
28458c2ecf20Sopenharmony_ci		return NULL;
28468c2ecf20Sopenharmony_ci	return list_next_entry(nh, router_list_node);
28478c2ecf20Sopenharmony_ci}
28488c2ecf20Sopenharmony_ci
28498c2ecf20Sopenharmony_cibool mlxsw_sp_nexthop_offload(struct mlxsw_sp_nexthop *nh)
28508c2ecf20Sopenharmony_ci{
28518c2ecf20Sopenharmony_ci	return nh->offloaded;
28528c2ecf20Sopenharmony_ci}
28538c2ecf20Sopenharmony_ci
28548c2ecf20Sopenharmony_ciunsigned char *mlxsw_sp_nexthop_ha(struct mlxsw_sp_nexthop *nh)
28558c2ecf20Sopenharmony_ci{
28568c2ecf20Sopenharmony_ci	if (!nh->offloaded)
28578c2ecf20Sopenharmony_ci		return NULL;
28588c2ecf20Sopenharmony_ci	return nh->neigh_entry->ha;
28598c2ecf20Sopenharmony_ci}
28608c2ecf20Sopenharmony_ci
28618c2ecf20Sopenharmony_ciint mlxsw_sp_nexthop_indexes(struct mlxsw_sp_nexthop *nh, u32 *p_adj_index,
28628c2ecf20Sopenharmony_ci			     u32 *p_adj_size, u32 *p_adj_hash_index)
28638c2ecf20Sopenharmony_ci{
28648c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group *nh_grp = nh->nh_grp;
28658c2ecf20Sopenharmony_ci	u32 adj_hash_index = 0;
28668c2ecf20Sopenharmony_ci	int i;
28678c2ecf20Sopenharmony_ci
28688c2ecf20Sopenharmony_ci	if (!nh->offloaded || !nh_grp->adj_index_valid)
28698c2ecf20Sopenharmony_ci		return -EINVAL;
28708c2ecf20Sopenharmony_ci
28718c2ecf20Sopenharmony_ci	*p_adj_index = nh_grp->adj_index;
28728c2ecf20Sopenharmony_ci	*p_adj_size = nh_grp->ecmp_size;
28738c2ecf20Sopenharmony_ci
28748c2ecf20Sopenharmony_ci	for (i = 0; i < nh_grp->count; i++) {
28758c2ecf20Sopenharmony_ci		struct mlxsw_sp_nexthop *nh_iter = &nh_grp->nexthops[i];
28768c2ecf20Sopenharmony_ci
28778c2ecf20Sopenharmony_ci		if (nh_iter == nh)
28788c2ecf20Sopenharmony_ci			break;
28798c2ecf20Sopenharmony_ci		if (nh_iter->offloaded)
28808c2ecf20Sopenharmony_ci			adj_hash_index += nh_iter->num_adj_entries;
28818c2ecf20Sopenharmony_ci	}
28828c2ecf20Sopenharmony_ci
28838c2ecf20Sopenharmony_ci	*p_adj_hash_index = adj_hash_index;
28848c2ecf20Sopenharmony_ci	return 0;
28858c2ecf20Sopenharmony_ci}
28868c2ecf20Sopenharmony_ci
28878c2ecf20Sopenharmony_cistruct mlxsw_sp_rif *mlxsw_sp_nexthop_rif(struct mlxsw_sp_nexthop *nh)
28888c2ecf20Sopenharmony_ci{
28898c2ecf20Sopenharmony_ci	return nh->rif;
28908c2ecf20Sopenharmony_ci}
28918c2ecf20Sopenharmony_ci
28928c2ecf20Sopenharmony_cibool mlxsw_sp_nexthop_group_has_ipip(struct mlxsw_sp_nexthop *nh)
28938c2ecf20Sopenharmony_ci{
28948c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group *nh_grp = nh->nh_grp;
28958c2ecf20Sopenharmony_ci	int i;
28968c2ecf20Sopenharmony_ci
28978c2ecf20Sopenharmony_ci	for (i = 0; i < nh_grp->count; i++) {
28988c2ecf20Sopenharmony_ci		struct mlxsw_sp_nexthop *nh_iter = &nh_grp->nexthops[i];
28998c2ecf20Sopenharmony_ci
29008c2ecf20Sopenharmony_ci		if (nh_iter->type == MLXSW_SP_NEXTHOP_TYPE_IPIP)
29018c2ecf20Sopenharmony_ci			return true;
29028c2ecf20Sopenharmony_ci	}
29038c2ecf20Sopenharmony_ci	return false;
29048c2ecf20Sopenharmony_ci}
29058c2ecf20Sopenharmony_ci
29068c2ecf20Sopenharmony_cistatic struct fib_info *
29078c2ecf20Sopenharmony_cimlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
29088c2ecf20Sopenharmony_ci{
29098c2ecf20Sopenharmony_ci	return nh_grp->priv;
29108c2ecf20Sopenharmony_ci}
29118c2ecf20Sopenharmony_ci
29128c2ecf20Sopenharmony_cistruct mlxsw_sp_nexthop_group_cmp_arg {
29138c2ecf20Sopenharmony_ci	enum mlxsw_sp_l3proto proto;
29148c2ecf20Sopenharmony_ci	union {
29158c2ecf20Sopenharmony_ci		struct fib_info *fi;
29168c2ecf20Sopenharmony_ci		struct mlxsw_sp_fib6_entry *fib6_entry;
29178c2ecf20Sopenharmony_ci	};
29188c2ecf20Sopenharmony_ci};
29198c2ecf20Sopenharmony_ci
29208c2ecf20Sopenharmony_cistatic bool
29218c2ecf20Sopenharmony_cimlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,
29228c2ecf20Sopenharmony_ci				    const struct in6_addr *gw, int ifindex,
29238c2ecf20Sopenharmony_ci				    int weight)
29248c2ecf20Sopenharmony_ci{
29258c2ecf20Sopenharmony_ci	int i;
29268c2ecf20Sopenharmony_ci
29278c2ecf20Sopenharmony_ci	for (i = 0; i < nh_grp->count; i++) {
29288c2ecf20Sopenharmony_ci		const struct mlxsw_sp_nexthop *nh;
29298c2ecf20Sopenharmony_ci
29308c2ecf20Sopenharmony_ci		nh = &nh_grp->nexthops[i];
29318c2ecf20Sopenharmony_ci		if (nh->ifindex == ifindex && nh->nh_weight == weight &&
29328c2ecf20Sopenharmony_ci		    ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))
29338c2ecf20Sopenharmony_ci			return true;
29348c2ecf20Sopenharmony_ci	}
29358c2ecf20Sopenharmony_ci
29368c2ecf20Sopenharmony_ci	return false;
29378c2ecf20Sopenharmony_ci}
29388c2ecf20Sopenharmony_ci
29398c2ecf20Sopenharmony_cistatic bool
29408c2ecf20Sopenharmony_cimlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
29418c2ecf20Sopenharmony_ci			    const struct mlxsw_sp_fib6_entry *fib6_entry)
29428c2ecf20Sopenharmony_ci{
29438c2ecf20Sopenharmony_ci	struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
29448c2ecf20Sopenharmony_ci
29458c2ecf20Sopenharmony_ci	if (nh_grp->count != fib6_entry->nrt6)
29468c2ecf20Sopenharmony_ci		return false;
29478c2ecf20Sopenharmony_ci
29488c2ecf20Sopenharmony_ci	list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
29498c2ecf20Sopenharmony_ci		struct fib6_nh *fib6_nh = mlxsw_sp_rt6->rt->fib6_nh;
29508c2ecf20Sopenharmony_ci		struct in6_addr *gw;
29518c2ecf20Sopenharmony_ci		int ifindex, weight;
29528c2ecf20Sopenharmony_ci
29538c2ecf20Sopenharmony_ci		ifindex = fib6_nh->fib_nh_dev->ifindex;
29548c2ecf20Sopenharmony_ci		weight = fib6_nh->fib_nh_weight;
29558c2ecf20Sopenharmony_ci		gw = &fib6_nh->fib_nh_gw6;
29568c2ecf20Sopenharmony_ci		if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex,
29578c2ecf20Sopenharmony_ci							 weight))
29588c2ecf20Sopenharmony_ci			return false;
29598c2ecf20Sopenharmony_ci	}
29608c2ecf20Sopenharmony_ci
29618c2ecf20Sopenharmony_ci	return true;
29628c2ecf20Sopenharmony_ci}
29638c2ecf20Sopenharmony_ci
29648c2ecf20Sopenharmony_cistatic int
29658c2ecf20Sopenharmony_cimlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
29668c2ecf20Sopenharmony_ci{
29678c2ecf20Sopenharmony_ci	const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
29688c2ecf20Sopenharmony_ci	const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
29698c2ecf20Sopenharmony_ci
29708c2ecf20Sopenharmony_ci	switch (cmp_arg->proto) {
29718c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
29728c2ecf20Sopenharmony_ci		return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
29738c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
29748c2ecf20Sopenharmony_ci		return !mlxsw_sp_nexthop6_group_cmp(nh_grp,
29758c2ecf20Sopenharmony_ci						    cmp_arg->fib6_entry);
29768c2ecf20Sopenharmony_ci	default:
29778c2ecf20Sopenharmony_ci		WARN_ON(1);
29788c2ecf20Sopenharmony_ci		return 1;
29798c2ecf20Sopenharmony_ci	}
29808c2ecf20Sopenharmony_ci}
29818c2ecf20Sopenharmony_ci
29828c2ecf20Sopenharmony_cistatic int
29838c2ecf20Sopenharmony_cimlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp)
29848c2ecf20Sopenharmony_ci{
29858c2ecf20Sopenharmony_ci	return nh_grp->neigh_tbl->family;
29868c2ecf20Sopenharmony_ci}
29878c2ecf20Sopenharmony_ci
29888c2ecf20Sopenharmony_cistatic u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
29898c2ecf20Sopenharmony_ci{
29908c2ecf20Sopenharmony_ci	const struct mlxsw_sp_nexthop_group *nh_grp = data;
29918c2ecf20Sopenharmony_ci	const struct mlxsw_sp_nexthop *nh;
29928c2ecf20Sopenharmony_ci	struct fib_info *fi;
29938c2ecf20Sopenharmony_ci	unsigned int val;
29948c2ecf20Sopenharmony_ci	int i;
29958c2ecf20Sopenharmony_ci
29968c2ecf20Sopenharmony_ci	switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
29978c2ecf20Sopenharmony_ci	case AF_INET:
29988c2ecf20Sopenharmony_ci		fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
29998c2ecf20Sopenharmony_ci		return jhash(&fi, sizeof(fi), seed);
30008c2ecf20Sopenharmony_ci	case AF_INET6:
30018c2ecf20Sopenharmony_ci		val = nh_grp->count;
30028c2ecf20Sopenharmony_ci		for (i = 0; i < nh_grp->count; i++) {
30038c2ecf20Sopenharmony_ci			nh = &nh_grp->nexthops[i];
30048c2ecf20Sopenharmony_ci			val ^= jhash(&nh->ifindex, sizeof(nh->ifindex), seed);
30058c2ecf20Sopenharmony_ci			val ^= jhash(&nh->gw_addr, sizeof(nh->gw_addr), seed);
30068c2ecf20Sopenharmony_ci		}
30078c2ecf20Sopenharmony_ci		return jhash(&val, sizeof(val), seed);
30088c2ecf20Sopenharmony_ci	default:
30098c2ecf20Sopenharmony_ci		WARN_ON(1);
30108c2ecf20Sopenharmony_ci		return 0;
30118c2ecf20Sopenharmony_ci	}
30128c2ecf20Sopenharmony_ci}
30138c2ecf20Sopenharmony_ci
30148c2ecf20Sopenharmony_cistatic u32
30158c2ecf20Sopenharmony_cimlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
30168c2ecf20Sopenharmony_ci{
30178c2ecf20Sopenharmony_ci	unsigned int val = fib6_entry->nrt6;
30188c2ecf20Sopenharmony_ci	struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
30198c2ecf20Sopenharmony_ci
30208c2ecf20Sopenharmony_ci	list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
30218c2ecf20Sopenharmony_ci		struct fib6_nh *fib6_nh = mlxsw_sp_rt6->rt->fib6_nh;
30228c2ecf20Sopenharmony_ci		struct net_device *dev = fib6_nh->fib_nh_dev;
30238c2ecf20Sopenharmony_ci		struct in6_addr *gw = &fib6_nh->fib_nh_gw6;
30248c2ecf20Sopenharmony_ci
30258c2ecf20Sopenharmony_ci		val ^= jhash(&dev->ifindex, sizeof(dev->ifindex), seed);
30268c2ecf20Sopenharmony_ci		val ^= jhash(gw, sizeof(*gw), seed);
30278c2ecf20Sopenharmony_ci	}
30288c2ecf20Sopenharmony_ci
30298c2ecf20Sopenharmony_ci	return jhash(&val, sizeof(val), seed);
30308c2ecf20Sopenharmony_ci}
30318c2ecf20Sopenharmony_ci
30328c2ecf20Sopenharmony_cistatic u32
30338c2ecf20Sopenharmony_cimlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
30348c2ecf20Sopenharmony_ci{
30358c2ecf20Sopenharmony_ci	const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
30368c2ecf20Sopenharmony_ci
30378c2ecf20Sopenharmony_ci	switch (cmp_arg->proto) {
30388c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
30398c2ecf20Sopenharmony_ci		return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
30408c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
30418c2ecf20Sopenharmony_ci		return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);
30428c2ecf20Sopenharmony_ci	default:
30438c2ecf20Sopenharmony_ci		WARN_ON(1);
30448c2ecf20Sopenharmony_ci		return 0;
30458c2ecf20Sopenharmony_ci	}
30468c2ecf20Sopenharmony_ci}
30478c2ecf20Sopenharmony_ci
30488c2ecf20Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
30498c2ecf20Sopenharmony_ci	.head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
30508c2ecf20Sopenharmony_ci	.hashfn	     = mlxsw_sp_nexthop_group_hash,
30518c2ecf20Sopenharmony_ci	.obj_hashfn  = mlxsw_sp_nexthop_group_hash_obj,
30528c2ecf20Sopenharmony_ci	.obj_cmpfn   = mlxsw_sp_nexthop_group_cmp,
30538c2ecf20Sopenharmony_ci};
30548c2ecf20Sopenharmony_ci
30558c2ecf20Sopenharmony_cistatic int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
30568c2ecf20Sopenharmony_ci					 struct mlxsw_sp_nexthop_group *nh_grp)
30578c2ecf20Sopenharmony_ci{
30588c2ecf20Sopenharmony_ci	if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
30598c2ecf20Sopenharmony_ci	    !nh_grp->gateway)
30608c2ecf20Sopenharmony_ci		return 0;
30618c2ecf20Sopenharmony_ci
30628c2ecf20Sopenharmony_ci	return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
30638c2ecf20Sopenharmony_ci				      &nh_grp->ht_node,
30648c2ecf20Sopenharmony_ci				      mlxsw_sp_nexthop_group_ht_params);
30658c2ecf20Sopenharmony_ci}
30668c2ecf20Sopenharmony_ci
30678c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
30688c2ecf20Sopenharmony_ci					  struct mlxsw_sp_nexthop_group *nh_grp)
30698c2ecf20Sopenharmony_ci{
30708c2ecf20Sopenharmony_ci	if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
30718c2ecf20Sopenharmony_ci	    !nh_grp->gateway)
30728c2ecf20Sopenharmony_ci		return;
30738c2ecf20Sopenharmony_ci
30748c2ecf20Sopenharmony_ci	rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
30758c2ecf20Sopenharmony_ci			       &nh_grp->ht_node,
30768c2ecf20Sopenharmony_ci			       mlxsw_sp_nexthop_group_ht_params);
30778c2ecf20Sopenharmony_ci}
30788c2ecf20Sopenharmony_ci
30798c2ecf20Sopenharmony_cistatic struct mlxsw_sp_nexthop_group *
30808c2ecf20Sopenharmony_cimlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
30818c2ecf20Sopenharmony_ci			       struct fib_info *fi)
30828c2ecf20Sopenharmony_ci{
30838c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
30848c2ecf20Sopenharmony_ci
30858c2ecf20Sopenharmony_ci	cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4;
30868c2ecf20Sopenharmony_ci	cmp_arg.fi = fi;
30878c2ecf20Sopenharmony_ci	return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
30888c2ecf20Sopenharmony_ci				      &cmp_arg,
30898c2ecf20Sopenharmony_ci				      mlxsw_sp_nexthop_group_ht_params);
30908c2ecf20Sopenharmony_ci}
30918c2ecf20Sopenharmony_ci
30928c2ecf20Sopenharmony_cistatic struct mlxsw_sp_nexthop_group *
30938c2ecf20Sopenharmony_cimlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,
30948c2ecf20Sopenharmony_ci			       struct mlxsw_sp_fib6_entry *fib6_entry)
30958c2ecf20Sopenharmony_ci{
30968c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
30978c2ecf20Sopenharmony_ci
30988c2ecf20Sopenharmony_ci	cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6;
30998c2ecf20Sopenharmony_ci	cmp_arg.fib6_entry = fib6_entry;
31008c2ecf20Sopenharmony_ci	return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
31018c2ecf20Sopenharmony_ci				      &cmp_arg,
31028c2ecf20Sopenharmony_ci				      mlxsw_sp_nexthop_group_ht_params);
31038c2ecf20Sopenharmony_ci}
31048c2ecf20Sopenharmony_ci
31058c2ecf20Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
31068c2ecf20Sopenharmony_ci	.key_offset = offsetof(struct mlxsw_sp_nexthop, key),
31078c2ecf20Sopenharmony_ci	.head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
31088c2ecf20Sopenharmony_ci	.key_len = sizeof(struct mlxsw_sp_nexthop_key),
31098c2ecf20Sopenharmony_ci};
31108c2ecf20Sopenharmony_ci
31118c2ecf20Sopenharmony_cistatic int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
31128c2ecf20Sopenharmony_ci				   struct mlxsw_sp_nexthop *nh)
31138c2ecf20Sopenharmony_ci{
31148c2ecf20Sopenharmony_ci	return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
31158c2ecf20Sopenharmony_ci				      &nh->ht_node, mlxsw_sp_nexthop_ht_params);
31168c2ecf20Sopenharmony_ci}
31178c2ecf20Sopenharmony_ci
31188c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
31198c2ecf20Sopenharmony_ci				    struct mlxsw_sp_nexthop *nh)
31208c2ecf20Sopenharmony_ci{
31218c2ecf20Sopenharmony_ci	rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
31228c2ecf20Sopenharmony_ci			       mlxsw_sp_nexthop_ht_params);
31238c2ecf20Sopenharmony_ci}
31248c2ecf20Sopenharmony_ci
31258c2ecf20Sopenharmony_cistatic struct mlxsw_sp_nexthop *
31268c2ecf20Sopenharmony_cimlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
31278c2ecf20Sopenharmony_ci			struct mlxsw_sp_nexthop_key key)
31288c2ecf20Sopenharmony_ci{
31298c2ecf20Sopenharmony_ci	return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
31308c2ecf20Sopenharmony_ci				      mlxsw_sp_nexthop_ht_params);
31318c2ecf20Sopenharmony_ci}
31328c2ecf20Sopenharmony_ci
31338c2ecf20Sopenharmony_cistatic int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
31348c2ecf20Sopenharmony_ci					     const struct mlxsw_sp_fib *fib,
31358c2ecf20Sopenharmony_ci					     u32 adj_index, u16 ecmp_size,
31368c2ecf20Sopenharmony_ci					     u32 new_adj_index,
31378c2ecf20Sopenharmony_ci					     u16 new_ecmp_size)
31388c2ecf20Sopenharmony_ci{
31398c2ecf20Sopenharmony_ci	char raleu_pl[MLXSW_REG_RALEU_LEN];
31408c2ecf20Sopenharmony_ci
31418c2ecf20Sopenharmony_ci	mlxsw_reg_raleu_pack(raleu_pl,
31428c2ecf20Sopenharmony_ci			     (enum mlxsw_reg_ralxx_protocol) fib->proto,
31438c2ecf20Sopenharmony_ci			     fib->vr->id, adj_index, ecmp_size, new_adj_index,
31448c2ecf20Sopenharmony_ci			     new_ecmp_size);
31458c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
31468c2ecf20Sopenharmony_ci}
31478c2ecf20Sopenharmony_ci
31488c2ecf20Sopenharmony_cistatic int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
31498c2ecf20Sopenharmony_ci					  struct mlxsw_sp_nexthop_group *nh_grp,
31508c2ecf20Sopenharmony_ci					  u32 old_adj_index, u16 old_ecmp_size)
31518c2ecf20Sopenharmony_ci{
31528c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_entry *fib_entry;
31538c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib = NULL;
31548c2ecf20Sopenharmony_ci	int err;
31558c2ecf20Sopenharmony_ci
31568c2ecf20Sopenharmony_ci	list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
31578c2ecf20Sopenharmony_ci		if (fib == fib_entry->fib_node->fib)
31588c2ecf20Sopenharmony_ci			continue;
31598c2ecf20Sopenharmony_ci		fib = fib_entry->fib_node->fib;
31608c2ecf20Sopenharmony_ci		err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
31618c2ecf20Sopenharmony_ci							old_adj_index,
31628c2ecf20Sopenharmony_ci							old_ecmp_size,
31638c2ecf20Sopenharmony_ci							nh_grp->adj_index,
31648c2ecf20Sopenharmony_ci							nh_grp->ecmp_size);
31658c2ecf20Sopenharmony_ci		if (err)
31668c2ecf20Sopenharmony_ci			return err;
31678c2ecf20Sopenharmony_ci	}
31688c2ecf20Sopenharmony_ci	return 0;
31698c2ecf20Sopenharmony_ci}
31708c2ecf20Sopenharmony_ci
31718c2ecf20Sopenharmony_cistatic int __mlxsw_sp_nexthop_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
31728c2ecf20Sopenharmony_ci				     struct mlxsw_sp_nexthop *nh)
31738c2ecf20Sopenharmony_ci{
31748c2ecf20Sopenharmony_ci	struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
31758c2ecf20Sopenharmony_ci	char ratr_pl[MLXSW_REG_RATR_LEN];
31768c2ecf20Sopenharmony_ci
31778c2ecf20Sopenharmony_ci	mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
31788c2ecf20Sopenharmony_ci			    true, MLXSW_REG_RATR_TYPE_ETHERNET,
31798c2ecf20Sopenharmony_ci			    adj_index, neigh_entry->rif);
31808c2ecf20Sopenharmony_ci	mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
31818c2ecf20Sopenharmony_ci	if (nh->counter_valid)
31828c2ecf20Sopenharmony_ci		mlxsw_reg_ratr_counter_pack(ratr_pl, nh->counter_index, true);
31838c2ecf20Sopenharmony_ci	else
31848c2ecf20Sopenharmony_ci		mlxsw_reg_ratr_counter_pack(ratr_pl, 0, false);
31858c2ecf20Sopenharmony_ci
31868c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
31878c2ecf20Sopenharmony_ci}
31888c2ecf20Sopenharmony_ci
31898c2ecf20Sopenharmony_ciint mlxsw_sp_nexthop_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
31908c2ecf20Sopenharmony_ci			    struct mlxsw_sp_nexthop *nh)
31918c2ecf20Sopenharmony_ci{
31928c2ecf20Sopenharmony_ci	int i;
31938c2ecf20Sopenharmony_ci
31948c2ecf20Sopenharmony_ci	for (i = 0; i < nh->num_adj_entries; i++) {
31958c2ecf20Sopenharmony_ci		int err;
31968c2ecf20Sopenharmony_ci
31978c2ecf20Sopenharmony_ci		err = __mlxsw_sp_nexthop_update(mlxsw_sp, adj_index + i, nh);
31988c2ecf20Sopenharmony_ci		if (err)
31998c2ecf20Sopenharmony_ci			return err;
32008c2ecf20Sopenharmony_ci	}
32018c2ecf20Sopenharmony_ci
32028c2ecf20Sopenharmony_ci	return 0;
32038c2ecf20Sopenharmony_ci}
32048c2ecf20Sopenharmony_ci
32058c2ecf20Sopenharmony_cistatic int __mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp,
32068c2ecf20Sopenharmony_ci					  u32 adj_index,
32078c2ecf20Sopenharmony_ci					  struct mlxsw_sp_nexthop *nh)
32088c2ecf20Sopenharmony_ci{
32098c2ecf20Sopenharmony_ci	const struct mlxsw_sp_ipip_ops *ipip_ops;
32108c2ecf20Sopenharmony_ci
32118c2ecf20Sopenharmony_ci	ipip_ops = mlxsw_sp->router->ipip_ops_arr[nh->ipip_entry->ipipt];
32128c2ecf20Sopenharmony_ci	return ipip_ops->nexthop_update(mlxsw_sp, adj_index, nh->ipip_entry);
32138c2ecf20Sopenharmony_ci}
32148c2ecf20Sopenharmony_ci
32158c2ecf20Sopenharmony_cistatic int mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp,
32168c2ecf20Sopenharmony_ci					u32 adj_index,
32178c2ecf20Sopenharmony_ci					struct mlxsw_sp_nexthop *nh)
32188c2ecf20Sopenharmony_ci{
32198c2ecf20Sopenharmony_ci	int i;
32208c2ecf20Sopenharmony_ci
32218c2ecf20Sopenharmony_ci	for (i = 0; i < nh->num_adj_entries; i++) {
32228c2ecf20Sopenharmony_ci		int err;
32238c2ecf20Sopenharmony_ci
32248c2ecf20Sopenharmony_ci		err = __mlxsw_sp_nexthop_ipip_update(mlxsw_sp, adj_index + i,
32258c2ecf20Sopenharmony_ci						     nh);
32268c2ecf20Sopenharmony_ci		if (err)
32278c2ecf20Sopenharmony_ci			return err;
32288c2ecf20Sopenharmony_ci	}
32298c2ecf20Sopenharmony_ci
32308c2ecf20Sopenharmony_ci	return 0;
32318c2ecf20Sopenharmony_ci}
32328c2ecf20Sopenharmony_ci
32338c2ecf20Sopenharmony_cistatic int
32348c2ecf20Sopenharmony_cimlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp,
32358c2ecf20Sopenharmony_ci			      struct mlxsw_sp_nexthop_group *nh_grp,
32368c2ecf20Sopenharmony_ci			      bool reallocate)
32378c2ecf20Sopenharmony_ci{
32388c2ecf20Sopenharmony_ci	u32 adj_index = nh_grp->adj_index; /* base */
32398c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop *nh;
32408c2ecf20Sopenharmony_ci	int i;
32418c2ecf20Sopenharmony_ci
32428c2ecf20Sopenharmony_ci	for (i = 0; i < nh_grp->count; i++) {
32438c2ecf20Sopenharmony_ci		nh = &nh_grp->nexthops[i];
32448c2ecf20Sopenharmony_ci
32458c2ecf20Sopenharmony_ci		if (!nh->should_offload) {
32468c2ecf20Sopenharmony_ci			nh->offloaded = 0;
32478c2ecf20Sopenharmony_ci			continue;
32488c2ecf20Sopenharmony_ci		}
32498c2ecf20Sopenharmony_ci
32508c2ecf20Sopenharmony_ci		if (nh->update || reallocate) {
32518c2ecf20Sopenharmony_ci			int err = 0;
32528c2ecf20Sopenharmony_ci
32538c2ecf20Sopenharmony_ci			switch (nh->type) {
32548c2ecf20Sopenharmony_ci			case MLXSW_SP_NEXTHOP_TYPE_ETH:
32558c2ecf20Sopenharmony_ci				err = mlxsw_sp_nexthop_update
32568c2ecf20Sopenharmony_ci					    (mlxsw_sp, adj_index, nh);
32578c2ecf20Sopenharmony_ci				break;
32588c2ecf20Sopenharmony_ci			case MLXSW_SP_NEXTHOP_TYPE_IPIP:
32598c2ecf20Sopenharmony_ci				err = mlxsw_sp_nexthop_ipip_update
32608c2ecf20Sopenharmony_ci					    (mlxsw_sp, adj_index, nh);
32618c2ecf20Sopenharmony_ci				break;
32628c2ecf20Sopenharmony_ci			}
32638c2ecf20Sopenharmony_ci			if (err)
32648c2ecf20Sopenharmony_ci				return err;
32658c2ecf20Sopenharmony_ci			nh->update = 0;
32668c2ecf20Sopenharmony_ci			nh->offloaded = 1;
32678c2ecf20Sopenharmony_ci		}
32688c2ecf20Sopenharmony_ci		adj_index += nh->num_adj_entries;
32698c2ecf20Sopenharmony_ci	}
32708c2ecf20Sopenharmony_ci	return 0;
32718c2ecf20Sopenharmony_ci}
32728c2ecf20Sopenharmony_ci
32738c2ecf20Sopenharmony_cistatic int
32748c2ecf20Sopenharmony_cimlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
32758c2ecf20Sopenharmony_ci				    struct mlxsw_sp_nexthop_group *nh_grp)
32768c2ecf20Sopenharmony_ci{
32778c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_entry *fib_entry;
32788c2ecf20Sopenharmony_ci	int err;
32798c2ecf20Sopenharmony_ci
32808c2ecf20Sopenharmony_ci	list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
32818c2ecf20Sopenharmony_ci		err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
32828c2ecf20Sopenharmony_ci		if (err)
32838c2ecf20Sopenharmony_ci			return err;
32848c2ecf20Sopenharmony_ci	}
32858c2ecf20Sopenharmony_ci	return 0;
32868c2ecf20Sopenharmony_ci}
32878c2ecf20Sopenharmony_ci
32888c2ecf20Sopenharmony_cistatic void mlxsw_sp_adj_grp_size_round_up(u16 *p_adj_grp_size)
32898c2ecf20Sopenharmony_ci{
32908c2ecf20Sopenharmony_ci	/* Valid sizes for an adjacency group are:
32918c2ecf20Sopenharmony_ci	 * 1-64, 512, 1024, 2048 and 4096.
32928c2ecf20Sopenharmony_ci	 */
32938c2ecf20Sopenharmony_ci	if (*p_adj_grp_size <= 64)
32948c2ecf20Sopenharmony_ci		return;
32958c2ecf20Sopenharmony_ci	else if (*p_adj_grp_size <= 512)
32968c2ecf20Sopenharmony_ci		*p_adj_grp_size = 512;
32978c2ecf20Sopenharmony_ci	else if (*p_adj_grp_size <= 1024)
32988c2ecf20Sopenharmony_ci		*p_adj_grp_size = 1024;
32998c2ecf20Sopenharmony_ci	else if (*p_adj_grp_size <= 2048)
33008c2ecf20Sopenharmony_ci		*p_adj_grp_size = 2048;
33018c2ecf20Sopenharmony_ci	else
33028c2ecf20Sopenharmony_ci		*p_adj_grp_size = 4096;
33038c2ecf20Sopenharmony_ci}
33048c2ecf20Sopenharmony_ci
33058c2ecf20Sopenharmony_cistatic void mlxsw_sp_adj_grp_size_round_down(u16 *p_adj_grp_size,
33068c2ecf20Sopenharmony_ci					     unsigned int alloc_size)
33078c2ecf20Sopenharmony_ci{
33088c2ecf20Sopenharmony_ci	if (alloc_size >= 4096)
33098c2ecf20Sopenharmony_ci		*p_adj_grp_size = 4096;
33108c2ecf20Sopenharmony_ci	else if (alloc_size >= 2048)
33118c2ecf20Sopenharmony_ci		*p_adj_grp_size = 2048;
33128c2ecf20Sopenharmony_ci	else if (alloc_size >= 1024)
33138c2ecf20Sopenharmony_ci		*p_adj_grp_size = 1024;
33148c2ecf20Sopenharmony_ci	else if (alloc_size >= 512)
33158c2ecf20Sopenharmony_ci		*p_adj_grp_size = 512;
33168c2ecf20Sopenharmony_ci}
33178c2ecf20Sopenharmony_ci
33188c2ecf20Sopenharmony_cistatic int mlxsw_sp_fix_adj_grp_size(struct mlxsw_sp *mlxsw_sp,
33198c2ecf20Sopenharmony_ci				     u16 *p_adj_grp_size)
33208c2ecf20Sopenharmony_ci{
33218c2ecf20Sopenharmony_ci	unsigned int alloc_size;
33228c2ecf20Sopenharmony_ci	int err;
33238c2ecf20Sopenharmony_ci
33248c2ecf20Sopenharmony_ci	/* Round up the requested group size to the next size supported
33258c2ecf20Sopenharmony_ci	 * by the device and make sure the request can be satisfied.
33268c2ecf20Sopenharmony_ci	 */
33278c2ecf20Sopenharmony_ci	mlxsw_sp_adj_grp_size_round_up(p_adj_grp_size);
33288c2ecf20Sopenharmony_ci	err = mlxsw_sp_kvdl_alloc_count_query(mlxsw_sp,
33298c2ecf20Sopenharmony_ci					      MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,
33308c2ecf20Sopenharmony_ci					      *p_adj_grp_size, &alloc_size);
33318c2ecf20Sopenharmony_ci	if (err)
33328c2ecf20Sopenharmony_ci		return err;
33338c2ecf20Sopenharmony_ci	/* It is possible the allocation results in more allocated
33348c2ecf20Sopenharmony_ci	 * entries than requested. Try to use as much of them as
33358c2ecf20Sopenharmony_ci	 * possible.
33368c2ecf20Sopenharmony_ci	 */
33378c2ecf20Sopenharmony_ci	mlxsw_sp_adj_grp_size_round_down(p_adj_grp_size, alloc_size);
33388c2ecf20Sopenharmony_ci
33398c2ecf20Sopenharmony_ci	return 0;
33408c2ecf20Sopenharmony_ci}
33418c2ecf20Sopenharmony_ci
33428c2ecf20Sopenharmony_cistatic void
33438c2ecf20Sopenharmony_cimlxsw_sp_nexthop_group_normalize(struct mlxsw_sp_nexthop_group *nh_grp)
33448c2ecf20Sopenharmony_ci{
33458c2ecf20Sopenharmony_ci	int i, g = 0, sum_norm_weight = 0;
33468c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop *nh;
33478c2ecf20Sopenharmony_ci
33488c2ecf20Sopenharmony_ci	for (i = 0; i < nh_grp->count; i++) {
33498c2ecf20Sopenharmony_ci		nh = &nh_grp->nexthops[i];
33508c2ecf20Sopenharmony_ci
33518c2ecf20Sopenharmony_ci		if (!nh->should_offload)
33528c2ecf20Sopenharmony_ci			continue;
33538c2ecf20Sopenharmony_ci		if (g > 0)
33548c2ecf20Sopenharmony_ci			g = gcd(nh->nh_weight, g);
33558c2ecf20Sopenharmony_ci		else
33568c2ecf20Sopenharmony_ci			g = nh->nh_weight;
33578c2ecf20Sopenharmony_ci	}
33588c2ecf20Sopenharmony_ci
33598c2ecf20Sopenharmony_ci	for (i = 0; i < nh_grp->count; i++) {
33608c2ecf20Sopenharmony_ci		nh = &nh_grp->nexthops[i];
33618c2ecf20Sopenharmony_ci
33628c2ecf20Sopenharmony_ci		if (!nh->should_offload)
33638c2ecf20Sopenharmony_ci			continue;
33648c2ecf20Sopenharmony_ci		nh->norm_nh_weight = nh->nh_weight / g;
33658c2ecf20Sopenharmony_ci		sum_norm_weight += nh->norm_nh_weight;
33668c2ecf20Sopenharmony_ci	}
33678c2ecf20Sopenharmony_ci
33688c2ecf20Sopenharmony_ci	nh_grp->sum_norm_weight = sum_norm_weight;
33698c2ecf20Sopenharmony_ci}
33708c2ecf20Sopenharmony_ci
33718c2ecf20Sopenharmony_cistatic void
33728c2ecf20Sopenharmony_cimlxsw_sp_nexthop_group_rebalance(struct mlxsw_sp_nexthop_group *nh_grp)
33738c2ecf20Sopenharmony_ci{
33748c2ecf20Sopenharmony_ci	int total = nh_grp->sum_norm_weight;
33758c2ecf20Sopenharmony_ci	u16 ecmp_size = nh_grp->ecmp_size;
33768c2ecf20Sopenharmony_ci	int i, weight = 0, lower_bound = 0;
33778c2ecf20Sopenharmony_ci
33788c2ecf20Sopenharmony_ci	for (i = 0; i < nh_grp->count; i++) {
33798c2ecf20Sopenharmony_ci		struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
33808c2ecf20Sopenharmony_ci		int upper_bound;
33818c2ecf20Sopenharmony_ci
33828c2ecf20Sopenharmony_ci		if (!nh->should_offload)
33838c2ecf20Sopenharmony_ci			continue;
33848c2ecf20Sopenharmony_ci		weight += nh->norm_nh_weight;
33858c2ecf20Sopenharmony_ci		upper_bound = DIV_ROUND_CLOSEST(ecmp_size * weight, total);
33868c2ecf20Sopenharmony_ci		nh->num_adj_entries = upper_bound - lower_bound;
33878c2ecf20Sopenharmony_ci		lower_bound = upper_bound;
33888c2ecf20Sopenharmony_ci	}
33898c2ecf20Sopenharmony_ci}
33908c2ecf20Sopenharmony_ci
33918c2ecf20Sopenharmony_cistatic struct mlxsw_sp_nexthop *
33928c2ecf20Sopenharmony_cimlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
33938c2ecf20Sopenharmony_ci		     const struct mlxsw_sp_rt6 *mlxsw_sp_rt6);
33948c2ecf20Sopenharmony_ci
33958c2ecf20Sopenharmony_cistatic void
33968c2ecf20Sopenharmony_cimlxsw_sp_nexthop4_group_offload_refresh(struct mlxsw_sp *mlxsw_sp,
33978c2ecf20Sopenharmony_ci					struct mlxsw_sp_nexthop_group *nh_grp)
33988c2ecf20Sopenharmony_ci{
33998c2ecf20Sopenharmony_ci	int i;
34008c2ecf20Sopenharmony_ci
34018c2ecf20Sopenharmony_ci	for (i = 0; i < nh_grp->count; i++) {
34028c2ecf20Sopenharmony_ci		struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
34038c2ecf20Sopenharmony_ci
34048c2ecf20Sopenharmony_ci		if (nh->offloaded)
34058c2ecf20Sopenharmony_ci			nh->key.fib_nh->fib_nh_flags |= RTNH_F_OFFLOAD;
34068c2ecf20Sopenharmony_ci		else
34078c2ecf20Sopenharmony_ci			nh->key.fib_nh->fib_nh_flags &= ~RTNH_F_OFFLOAD;
34088c2ecf20Sopenharmony_ci	}
34098c2ecf20Sopenharmony_ci}
34108c2ecf20Sopenharmony_ci
34118c2ecf20Sopenharmony_cistatic void
34128c2ecf20Sopenharmony_ci__mlxsw_sp_nexthop6_group_offload_refresh(struct mlxsw_sp_nexthop_group *nh_grp,
34138c2ecf20Sopenharmony_ci					  struct mlxsw_sp_fib6_entry *fib6_entry)
34148c2ecf20Sopenharmony_ci{
34158c2ecf20Sopenharmony_ci	struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
34168c2ecf20Sopenharmony_ci
34178c2ecf20Sopenharmony_ci	list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
34188c2ecf20Sopenharmony_ci		struct fib6_nh *fib6_nh = mlxsw_sp_rt6->rt->fib6_nh;
34198c2ecf20Sopenharmony_ci		struct mlxsw_sp_nexthop *nh;
34208c2ecf20Sopenharmony_ci
34218c2ecf20Sopenharmony_ci		nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
34228c2ecf20Sopenharmony_ci		if (nh && nh->offloaded)
34238c2ecf20Sopenharmony_ci			fib6_nh->fib_nh_flags |= RTNH_F_OFFLOAD;
34248c2ecf20Sopenharmony_ci		else
34258c2ecf20Sopenharmony_ci			fib6_nh->fib_nh_flags &= ~RTNH_F_OFFLOAD;
34268c2ecf20Sopenharmony_ci	}
34278c2ecf20Sopenharmony_ci}
34288c2ecf20Sopenharmony_ci
34298c2ecf20Sopenharmony_cistatic void
34308c2ecf20Sopenharmony_cimlxsw_sp_nexthop6_group_offload_refresh(struct mlxsw_sp *mlxsw_sp,
34318c2ecf20Sopenharmony_ci					struct mlxsw_sp_nexthop_group *nh_grp)
34328c2ecf20Sopenharmony_ci{
34338c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib6_entry *fib6_entry;
34348c2ecf20Sopenharmony_ci
34358c2ecf20Sopenharmony_ci	/* Unfortunately, in IPv6 the route and the nexthop are described by
34368c2ecf20Sopenharmony_ci	 * the same struct, so we need to iterate over all the routes using the
34378c2ecf20Sopenharmony_ci	 * nexthop group and set / clear the offload indication for them.
34388c2ecf20Sopenharmony_ci	 */
34398c2ecf20Sopenharmony_ci	list_for_each_entry(fib6_entry, &nh_grp->fib_list,
34408c2ecf20Sopenharmony_ci			    common.nexthop_group_node)
34418c2ecf20Sopenharmony_ci		__mlxsw_sp_nexthop6_group_offload_refresh(nh_grp, fib6_entry);
34428c2ecf20Sopenharmony_ci}
34438c2ecf20Sopenharmony_ci
34448c2ecf20Sopenharmony_cistatic void
34458c2ecf20Sopenharmony_cimlxsw_sp_nexthop_group_offload_refresh(struct mlxsw_sp *mlxsw_sp,
34468c2ecf20Sopenharmony_ci				       struct mlxsw_sp_nexthop_group *nh_grp)
34478c2ecf20Sopenharmony_ci{
34488c2ecf20Sopenharmony_ci	switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
34498c2ecf20Sopenharmony_ci	case AF_INET:
34508c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop4_group_offload_refresh(mlxsw_sp, nh_grp);
34518c2ecf20Sopenharmony_ci		break;
34528c2ecf20Sopenharmony_ci	case AF_INET6:
34538c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop6_group_offload_refresh(mlxsw_sp, nh_grp);
34548c2ecf20Sopenharmony_ci		break;
34558c2ecf20Sopenharmony_ci	}
34568c2ecf20Sopenharmony_ci}
34578c2ecf20Sopenharmony_ci
34588c2ecf20Sopenharmony_cistatic void
34598c2ecf20Sopenharmony_cimlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
34608c2ecf20Sopenharmony_ci			       struct mlxsw_sp_nexthop_group *nh_grp)
34618c2ecf20Sopenharmony_ci{
34628c2ecf20Sopenharmony_ci	u16 ecmp_size, old_ecmp_size;
34638c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop *nh;
34648c2ecf20Sopenharmony_ci	bool offload_change = false;
34658c2ecf20Sopenharmony_ci	u32 adj_index;
34668c2ecf20Sopenharmony_ci	bool old_adj_index_valid;
34678c2ecf20Sopenharmony_ci	u32 old_adj_index;
34688c2ecf20Sopenharmony_ci	int i;
34698c2ecf20Sopenharmony_ci	int err;
34708c2ecf20Sopenharmony_ci
34718c2ecf20Sopenharmony_ci	if (!nh_grp->gateway) {
34728c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
34738c2ecf20Sopenharmony_ci		return;
34748c2ecf20Sopenharmony_ci	}
34758c2ecf20Sopenharmony_ci
34768c2ecf20Sopenharmony_ci	for (i = 0; i < nh_grp->count; i++) {
34778c2ecf20Sopenharmony_ci		nh = &nh_grp->nexthops[i];
34788c2ecf20Sopenharmony_ci
34798c2ecf20Sopenharmony_ci		if (nh->should_offload != nh->offloaded) {
34808c2ecf20Sopenharmony_ci			offload_change = true;
34818c2ecf20Sopenharmony_ci			if (nh->should_offload)
34828c2ecf20Sopenharmony_ci				nh->update = 1;
34838c2ecf20Sopenharmony_ci		}
34848c2ecf20Sopenharmony_ci	}
34858c2ecf20Sopenharmony_ci	if (!offload_change) {
34868c2ecf20Sopenharmony_ci		/* Nothing was added or removed, so no need to reallocate. Just
34878c2ecf20Sopenharmony_ci		 * update MAC on existing adjacency indexes.
34888c2ecf20Sopenharmony_ci		 */
34898c2ecf20Sopenharmony_ci		err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, false);
34908c2ecf20Sopenharmony_ci		if (err) {
34918c2ecf20Sopenharmony_ci			dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
34928c2ecf20Sopenharmony_ci			goto set_trap;
34938c2ecf20Sopenharmony_ci		}
34948c2ecf20Sopenharmony_ci		return;
34958c2ecf20Sopenharmony_ci	}
34968c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_group_normalize(nh_grp);
34978c2ecf20Sopenharmony_ci	if (!nh_grp->sum_norm_weight)
34988c2ecf20Sopenharmony_ci		/* No neigh of this group is connected so we just set
34998c2ecf20Sopenharmony_ci		 * the trap and let everthing flow through kernel.
35008c2ecf20Sopenharmony_ci		 */
35018c2ecf20Sopenharmony_ci		goto set_trap;
35028c2ecf20Sopenharmony_ci
35038c2ecf20Sopenharmony_ci	ecmp_size = nh_grp->sum_norm_weight;
35048c2ecf20Sopenharmony_ci	err = mlxsw_sp_fix_adj_grp_size(mlxsw_sp, &ecmp_size);
35058c2ecf20Sopenharmony_ci	if (err)
35068c2ecf20Sopenharmony_ci		/* No valid allocation size available. */
35078c2ecf20Sopenharmony_ci		goto set_trap;
35088c2ecf20Sopenharmony_ci
35098c2ecf20Sopenharmony_ci	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,
35108c2ecf20Sopenharmony_ci				  ecmp_size, &adj_index);
35118c2ecf20Sopenharmony_ci	if (err) {
35128c2ecf20Sopenharmony_ci		/* We ran out of KVD linear space, just set the
35138c2ecf20Sopenharmony_ci		 * trap and let everything flow through kernel.
35148c2ecf20Sopenharmony_ci		 */
35158c2ecf20Sopenharmony_ci		dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
35168c2ecf20Sopenharmony_ci		goto set_trap;
35178c2ecf20Sopenharmony_ci	}
35188c2ecf20Sopenharmony_ci	old_adj_index_valid = nh_grp->adj_index_valid;
35198c2ecf20Sopenharmony_ci	old_adj_index = nh_grp->adj_index;
35208c2ecf20Sopenharmony_ci	old_ecmp_size = nh_grp->ecmp_size;
35218c2ecf20Sopenharmony_ci	nh_grp->adj_index_valid = 1;
35228c2ecf20Sopenharmony_ci	nh_grp->adj_index = adj_index;
35238c2ecf20Sopenharmony_ci	nh_grp->ecmp_size = ecmp_size;
35248c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_group_rebalance(nh_grp);
35258c2ecf20Sopenharmony_ci	err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, true);
35268c2ecf20Sopenharmony_ci	if (err) {
35278c2ecf20Sopenharmony_ci		dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
35288c2ecf20Sopenharmony_ci		goto set_trap;
35298c2ecf20Sopenharmony_ci	}
35308c2ecf20Sopenharmony_ci
35318c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_group_offload_refresh(mlxsw_sp, nh_grp);
35328c2ecf20Sopenharmony_ci
35338c2ecf20Sopenharmony_ci	if (!old_adj_index_valid) {
35348c2ecf20Sopenharmony_ci		/* The trap was set for fib entries, so we have to call
35358c2ecf20Sopenharmony_ci		 * fib entry update to unset it and use adjacency index.
35368c2ecf20Sopenharmony_ci		 */
35378c2ecf20Sopenharmony_ci		err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
35388c2ecf20Sopenharmony_ci		if (err) {
35398c2ecf20Sopenharmony_ci			dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
35408c2ecf20Sopenharmony_ci			goto set_trap;
35418c2ecf20Sopenharmony_ci		}
35428c2ecf20Sopenharmony_ci		return;
35438c2ecf20Sopenharmony_ci	}
35448c2ecf20Sopenharmony_ci
35458c2ecf20Sopenharmony_ci	err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
35468c2ecf20Sopenharmony_ci					     old_adj_index, old_ecmp_size);
35478c2ecf20Sopenharmony_ci	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,
35488c2ecf20Sopenharmony_ci			   old_ecmp_size, old_adj_index);
35498c2ecf20Sopenharmony_ci	if (err) {
35508c2ecf20Sopenharmony_ci		dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
35518c2ecf20Sopenharmony_ci		goto set_trap;
35528c2ecf20Sopenharmony_ci	}
35538c2ecf20Sopenharmony_ci
35548c2ecf20Sopenharmony_ci	return;
35558c2ecf20Sopenharmony_ci
35568c2ecf20Sopenharmony_ciset_trap:
35578c2ecf20Sopenharmony_ci	old_adj_index_valid = nh_grp->adj_index_valid;
35588c2ecf20Sopenharmony_ci	nh_grp->adj_index_valid = 0;
35598c2ecf20Sopenharmony_ci	for (i = 0; i < nh_grp->count; i++) {
35608c2ecf20Sopenharmony_ci		nh = &nh_grp->nexthops[i];
35618c2ecf20Sopenharmony_ci		nh->offloaded = 0;
35628c2ecf20Sopenharmony_ci	}
35638c2ecf20Sopenharmony_ci	err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
35648c2ecf20Sopenharmony_ci	if (err)
35658c2ecf20Sopenharmony_ci		dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
35668c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_group_offload_refresh(mlxsw_sp, nh_grp);
35678c2ecf20Sopenharmony_ci	if (old_adj_index_valid)
35688c2ecf20Sopenharmony_ci		mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,
35698c2ecf20Sopenharmony_ci				   nh_grp->ecmp_size, nh_grp->adj_index);
35708c2ecf20Sopenharmony_ci}
35718c2ecf20Sopenharmony_ci
35728c2ecf20Sopenharmony_cistatic void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
35738c2ecf20Sopenharmony_ci					    bool removing)
35748c2ecf20Sopenharmony_ci{
35758c2ecf20Sopenharmony_ci	if (!removing)
35768c2ecf20Sopenharmony_ci		nh->should_offload = 1;
35778c2ecf20Sopenharmony_ci	else
35788c2ecf20Sopenharmony_ci		nh->should_offload = 0;
35798c2ecf20Sopenharmony_ci	nh->update = 1;
35808c2ecf20Sopenharmony_ci}
35818c2ecf20Sopenharmony_ci
35828c2ecf20Sopenharmony_cistatic int
35838c2ecf20Sopenharmony_cimlxsw_sp_nexthop_dead_neigh_replace(struct mlxsw_sp *mlxsw_sp,
35848c2ecf20Sopenharmony_ci				    struct mlxsw_sp_neigh_entry *neigh_entry)
35858c2ecf20Sopenharmony_ci{
35868c2ecf20Sopenharmony_ci	struct neighbour *n, *old_n = neigh_entry->key.n;
35878c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop *nh;
35888c2ecf20Sopenharmony_ci	bool entry_connected;
35898c2ecf20Sopenharmony_ci	u8 nud_state, dead;
35908c2ecf20Sopenharmony_ci	int err;
35918c2ecf20Sopenharmony_ci
35928c2ecf20Sopenharmony_ci	nh = list_first_entry(&neigh_entry->nexthop_list,
35938c2ecf20Sopenharmony_ci			      struct mlxsw_sp_nexthop, neigh_list_node);
35948c2ecf20Sopenharmony_ci
35958c2ecf20Sopenharmony_ci	n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
35968c2ecf20Sopenharmony_ci	if (!n) {
35978c2ecf20Sopenharmony_ci		n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
35988c2ecf20Sopenharmony_ci				 nh->rif->dev);
35998c2ecf20Sopenharmony_ci		if (IS_ERR(n))
36008c2ecf20Sopenharmony_ci			return PTR_ERR(n);
36018c2ecf20Sopenharmony_ci		neigh_event_send(n, NULL);
36028c2ecf20Sopenharmony_ci	}
36038c2ecf20Sopenharmony_ci
36048c2ecf20Sopenharmony_ci	mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
36058c2ecf20Sopenharmony_ci	neigh_entry->key.n = n;
36068c2ecf20Sopenharmony_ci	err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
36078c2ecf20Sopenharmony_ci	if (err)
36088c2ecf20Sopenharmony_ci		goto err_neigh_entry_insert;
36098c2ecf20Sopenharmony_ci
36108c2ecf20Sopenharmony_ci	read_lock_bh(&n->lock);
36118c2ecf20Sopenharmony_ci	nud_state = n->nud_state;
36128c2ecf20Sopenharmony_ci	dead = n->dead;
36138c2ecf20Sopenharmony_ci	read_unlock_bh(&n->lock);
36148c2ecf20Sopenharmony_ci	entry_connected = nud_state & NUD_VALID && !dead;
36158c2ecf20Sopenharmony_ci
36168c2ecf20Sopenharmony_ci	list_for_each_entry(nh, &neigh_entry->nexthop_list,
36178c2ecf20Sopenharmony_ci			    neigh_list_node) {
36188c2ecf20Sopenharmony_ci		neigh_release(old_n);
36198c2ecf20Sopenharmony_ci		neigh_clone(n);
36208c2ecf20Sopenharmony_ci		__mlxsw_sp_nexthop_neigh_update(nh, !entry_connected);
36218c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
36228c2ecf20Sopenharmony_ci	}
36238c2ecf20Sopenharmony_ci
36248c2ecf20Sopenharmony_ci	neigh_release(n);
36258c2ecf20Sopenharmony_ci
36268c2ecf20Sopenharmony_ci	return 0;
36278c2ecf20Sopenharmony_ci
36288c2ecf20Sopenharmony_cierr_neigh_entry_insert:
36298c2ecf20Sopenharmony_ci	neigh_entry->key.n = old_n;
36308c2ecf20Sopenharmony_ci	mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
36318c2ecf20Sopenharmony_ci	neigh_release(n);
36328c2ecf20Sopenharmony_ci	return err;
36338c2ecf20Sopenharmony_ci}
36348c2ecf20Sopenharmony_ci
36358c2ecf20Sopenharmony_cistatic void
36368c2ecf20Sopenharmony_cimlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
36378c2ecf20Sopenharmony_ci			      struct mlxsw_sp_neigh_entry *neigh_entry,
36388c2ecf20Sopenharmony_ci			      bool removing, bool dead)
36398c2ecf20Sopenharmony_ci{
36408c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop *nh;
36418c2ecf20Sopenharmony_ci
36428c2ecf20Sopenharmony_ci	if (list_empty(&neigh_entry->nexthop_list))
36438c2ecf20Sopenharmony_ci		return;
36448c2ecf20Sopenharmony_ci
36458c2ecf20Sopenharmony_ci	if (dead) {
36468c2ecf20Sopenharmony_ci		int err;
36478c2ecf20Sopenharmony_ci
36488c2ecf20Sopenharmony_ci		err = mlxsw_sp_nexthop_dead_neigh_replace(mlxsw_sp,
36498c2ecf20Sopenharmony_ci							  neigh_entry);
36508c2ecf20Sopenharmony_ci		if (err)
36518c2ecf20Sopenharmony_ci			dev_err(mlxsw_sp->bus_info->dev, "Failed to replace dead neigh\n");
36528c2ecf20Sopenharmony_ci		return;
36538c2ecf20Sopenharmony_ci	}
36548c2ecf20Sopenharmony_ci
36558c2ecf20Sopenharmony_ci	list_for_each_entry(nh, &neigh_entry->nexthop_list,
36568c2ecf20Sopenharmony_ci			    neigh_list_node) {
36578c2ecf20Sopenharmony_ci		__mlxsw_sp_nexthop_neigh_update(nh, removing);
36588c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
36598c2ecf20Sopenharmony_ci	}
36608c2ecf20Sopenharmony_ci}
36618c2ecf20Sopenharmony_ci
36628c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
36638c2ecf20Sopenharmony_ci				      struct mlxsw_sp_rif *rif)
36648c2ecf20Sopenharmony_ci{
36658c2ecf20Sopenharmony_ci	if (nh->rif)
36668c2ecf20Sopenharmony_ci		return;
36678c2ecf20Sopenharmony_ci
36688c2ecf20Sopenharmony_ci	nh->rif = rif;
36698c2ecf20Sopenharmony_ci	list_add(&nh->rif_list_node, &rif->nexthop_list);
36708c2ecf20Sopenharmony_ci}
36718c2ecf20Sopenharmony_ci
36728c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
36738c2ecf20Sopenharmony_ci{
36748c2ecf20Sopenharmony_ci	if (!nh->rif)
36758c2ecf20Sopenharmony_ci		return;
36768c2ecf20Sopenharmony_ci
36778c2ecf20Sopenharmony_ci	list_del(&nh->rif_list_node);
36788c2ecf20Sopenharmony_ci	nh->rif = NULL;
36798c2ecf20Sopenharmony_ci}
36808c2ecf20Sopenharmony_ci
36818c2ecf20Sopenharmony_cistatic int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
36828c2ecf20Sopenharmony_ci				       struct mlxsw_sp_nexthop *nh)
36838c2ecf20Sopenharmony_ci{
36848c2ecf20Sopenharmony_ci	struct mlxsw_sp_neigh_entry *neigh_entry;
36858c2ecf20Sopenharmony_ci	struct neighbour *n;
36868c2ecf20Sopenharmony_ci	u8 nud_state, dead;
36878c2ecf20Sopenharmony_ci	int err;
36888c2ecf20Sopenharmony_ci
36898c2ecf20Sopenharmony_ci	if (!nh->nh_grp->gateway || nh->neigh_entry)
36908c2ecf20Sopenharmony_ci		return 0;
36918c2ecf20Sopenharmony_ci
36928c2ecf20Sopenharmony_ci	/* Take a reference of neigh here ensuring that neigh would
36938c2ecf20Sopenharmony_ci	 * not be destructed before the nexthop entry is finished.
36948c2ecf20Sopenharmony_ci	 * The reference is taken either in neigh_lookup() or
36958c2ecf20Sopenharmony_ci	 * in neigh_create() in case n is not found.
36968c2ecf20Sopenharmony_ci	 */
36978c2ecf20Sopenharmony_ci	n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
36988c2ecf20Sopenharmony_ci	if (!n) {
36998c2ecf20Sopenharmony_ci		n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
37008c2ecf20Sopenharmony_ci				 nh->rif->dev);
37018c2ecf20Sopenharmony_ci		if (IS_ERR(n))
37028c2ecf20Sopenharmony_ci			return PTR_ERR(n);
37038c2ecf20Sopenharmony_ci		neigh_event_send(n, NULL);
37048c2ecf20Sopenharmony_ci	}
37058c2ecf20Sopenharmony_ci	neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
37068c2ecf20Sopenharmony_ci	if (!neigh_entry) {
37078c2ecf20Sopenharmony_ci		neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
37088c2ecf20Sopenharmony_ci		if (IS_ERR(neigh_entry)) {
37098c2ecf20Sopenharmony_ci			err = -EINVAL;
37108c2ecf20Sopenharmony_ci			goto err_neigh_entry_create;
37118c2ecf20Sopenharmony_ci		}
37128c2ecf20Sopenharmony_ci	}
37138c2ecf20Sopenharmony_ci
37148c2ecf20Sopenharmony_ci	/* If that is the first nexthop connected to that neigh, add to
37158c2ecf20Sopenharmony_ci	 * nexthop_neighs_list
37168c2ecf20Sopenharmony_ci	 */
37178c2ecf20Sopenharmony_ci	if (list_empty(&neigh_entry->nexthop_list))
37188c2ecf20Sopenharmony_ci		list_add_tail(&neigh_entry->nexthop_neighs_list_node,
37198c2ecf20Sopenharmony_ci			      &mlxsw_sp->router->nexthop_neighs_list);
37208c2ecf20Sopenharmony_ci
37218c2ecf20Sopenharmony_ci	nh->neigh_entry = neigh_entry;
37228c2ecf20Sopenharmony_ci	list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
37238c2ecf20Sopenharmony_ci	read_lock_bh(&n->lock);
37248c2ecf20Sopenharmony_ci	nud_state = n->nud_state;
37258c2ecf20Sopenharmony_ci	dead = n->dead;
37268c2ecf20Sopenharmony_ci	read_unlock_bh(&n->lock);
37278c2ecf20Sopenharmony_ci	__mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
37288c2ecf20Sopenharmony_ci
37298c2ecf20Sopenharmony_ci	return 0;
37308c2ecf20Sopenharmony_ci
37318c2ecf20Sopenharmony_cierr_neigh_entry_create:
37328c2ecf20Sopenharmony_ci	neigh_release(n);
37338c2ecf20Sopenharmony_ci	return err;
37348c2ecf20Sopenharmony_ci}
37358c2ecf20Sopenharmony_ci
37368c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
37378c2ecf20Sopenharmony_ci					struct mlxsw_sp_nexthop *nh)
37388c2ecf20Sopenharmony_ci{
37398c2ecf20Sopenharmony_ci	struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
37408c2ecf20Sopenharmony_ci	struct neighbour *n;
37418c2ecf20Sopenharmony_ci
37428c2ecf20Sopenharmony_ci	if (!neigh_entry)
37438c2ecf20Sopenharmony_ci		return;
37448c2ecf20Sopenharmony_ci	n = neigh_entry->key.n;
37458c2ecf20Sopenharmony_ci
37468c2ecf20Sopenharmony_ci	__mlxsw_sp_nexthop_neigh_update(nh, true);
37478c2ecf20Sopenharmony_ci	list_del(&nh->neigh_list_node);
37488c2ecf20Sopenharmony_ci	nh->neigh_entry = NULL;
37498c2ecf20Sopenharmony_ci
37508c2ecf20Sopenharmony_ci	/* If that is the last nexthop connected to that neigh, remove from
37518c2ecf20Sopenharmony_ci	 * nexthop_neighs_list
37528c2ecf20Sopenharmony_ci	 */
37538c2ecf20Sopenharmony_ci	if (list_empty(&neigh_entry->nexthop_list))
37548c2ecf20Sopenharmony_ci		list_del(&neigh_entry->nexthop_neighs_list_node);
37558c2ecf20Sopenharmony_ci
37568c2ecf20Sopenharmony_ci	if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
37578c2ecf20Sopenharmony_ci		mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
37588c2ecf20Sopenharmony_ci
37598c2ecf20Sopenharmony_ci	neigh_release(n);
37608c2ecf20Sopenharmony_ci}
37618c2ecf20Sopenharmony_ci
37628c2ecf20Sopenharmony_cistatic bool mlxsw_sp_ipip_netdev_ul_up(struct net_device *ol_dev)
37638c2ecf20Sopenharmony_ci{
37648c2ecf20Sopenharmony_ci	struct net_device *ul_dev;
37658c2ecf20Sopenharmony_ci	bool is_up;
37668c2ecf20Sopenharmony_ci
37678c2ecf20Sopenharmony_ci	rcu_read_lock();
37688c2ecf20Sopenharmony_ci	ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);
37698c2ecf20Sopenharmony_ci	is_up = ul_dev ? (ul_dev->flags & IFF_UP) : true;
37708c2ecf20Sopenharmony_ci	rcu_read_unlock();
37718c2ecf20Sopenharmony_ci
37728c2ecf20Sopenharmony_ci	return is_up;
37738c2ecf20Sopenharmony_ci}
37748c2ecf20Sopenharmony_ci
37758c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop_ipip_init(struct mlxsw_sp *mlxsw_sp,
37768c2ecf20Sopenharmony_ci				       struct mlxsw_sp_nexthop *nh,
37778c2ecf20Sopenharmony_ci				       struct mlxsw_sp_ipip_entry *ipip_entry)
37788c2ecf20Sopenharmony_ci{
37798c2ecf20Sopenharmony_ci	bool removing;
37808c2ecf20Sopenharmony_ci
37818c2ecf20Sopenharmony_ci	if (!nh->nh_grp->gateway || nh->ipip_entry)
37828c2ecf20Sopenharmony_ci		return;
37838c2ecf20Sopenharmony_ci
37848c2ecf20Sopenharmony_ci	nh->ipip_entry = ipip_entry;
37858c2ecf20Sopenharmony_ci	removing = !mlxsw_sp_ipip_netdev_ul_up(ipip_entry->ol_dev);
37868c2ecf20Sopenharmony_ci	__mlxsw_sp_nexthop_neigh_update(nh, removing);
37878c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_rif_init(nh, &ipip_entry->ol_lb->common);
37888c2ecf20Sopenharmony_ci}
37898c2ecf20Sopenharmony_ci
37908c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop_ipip_fini(struct mlxsw_sp *mlxsw_sp,
37918c2ecf20Sopenharmony_ci				       struct mlxsw_sp_nexthop *nh)
37928c2ecf20Sopenharmony_ci{
37938c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry = nh->ipip_entry;
37948c2ecf20Sopenharmony_ci
37958c2ecf20Sopenharmony_ci	if (!ipip_entry)
37968c2ecf20Sopenharmony_ci		return;
37978c2ecf20Sopenharmony_ci
37988c2ecf20Sopenharmony_ci	__mlxsw_sp_nexthop_neigh_update(nh, true);
37998c2ecf20Sopenharmony_ci	nh->ipip_entry = NULL;
38008c2ecf20Sopenharmony_ci}
38018c2ecf20Sopenharmony_ci
38028c2ecf20Sopenharmony_cistatic bool mlxsw_sp_nexthop4_ipip_type(const struct mlxsw_sp *mlxsw_sp,
38038c2ecf20Sopenharmony_ci					const struct fib_nh *fib_nh,
38048c2ecf20Sopenharmony_ci					enum mlxsw_sp_ipip_type *p_ipipt)
38058c2ecf20Sopenharmony_ci{
38068c2ecf20Sopenharmony_ci	struct net_device *dev = fib_nh->fib_nh_dev;
38078c2ecf20Sopenharmony_ci
38088c2ecf20Sopenharmony_ci	return dev &&
38098c2ecf20Sopenharmony_ci	       fib_nh->nh_parent->fib_type == RTN_UNICAST &&
38108c2ecf20Sopenharmony_ci	       mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, p_ipipt);
38118c2ecf20Sopenharmony_ci}
38128c2ecf20Sopenharmony_ci
38138c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp,
38148c2ecf20Sopenharmony_ci				       struct mlxsw_sp_nexthop *nh)
38158c2ecf20Sopenharmony_ci{
38168c2ecf20Sopenharmony_ci	switch (nh->type) {
38178c2ecf20Sopenharmony_ci	case MLXSW_SP_NEXTHOP_TYPE_ETH:
38188c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
38198c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop_rif_fini(nh);
38208c2ecf20Sopenharmony_ci		break;
38218c2ecf20Sopenharmony_ci	case MLXSW_SP_NEXTHOP_TYPE_IPIP:
38228c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop_rif_fini(nh);
38238c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh);
38248c2ecf20Sopenharmony_ci		break;
38258c2ecf20Sopenharmony_ci	}
38268c2ecf20Sopenharmony_ci}
38278c2ecf20Sopenharmony_ci
38288c2ecf20Sopenharmony_cistatic int mlxsw_sp_nexthop4_type_init(struct mlxsw_sp *mlxsw_sp,
38298c2ecf20Sopenharmony_ci				       struct mlxsw_sp_nexthop *nh,
38308c2ecf20Sopenharmony_ci				       struct fib_nh *fib_nh)
38318c2ecf20Sopenharmony_ci{
38328c2ecf20Sopenharmony_ci	const struct mlxsw_sp_ipip_ops *ipip_ops;
38338c2ecf20Sopenharmony_ci	struct net_device *dev = fib_nh->fib_nh_dev;
38348c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
38358c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
38368c2ecf20Sopenharmony_ci	int err;
38378c2ecf20Sopenharmony_ci
38388c2ecf20Sopenharmony_ci	ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, dev);
38398c2ecf20Sopenharmony_ci	if (ipip_entry) {
38408c2ecf20Sopenharmony_ci		ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
38418c2ecf20Sopenharmony_ci		if (ipip_ops->can_offload(mlxsw_sp, dev,
38428c2ecf20Sopenharmony_ci					  MLXSW_SP_L3_PROTO_IPV4)) {
38438c2ecf20Sopenharmony_ci			nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
38448c2ecf20Sopenharmony_ci			mlxsw_sp_nexthop_ipip_init(mlxsw_sp, nh, ipip_entry);
38458c2ecf20Sopenharmony_ci			return 0;
38468c2ecf20Sopenharmony_ci		}
38478c2ecf20Sopenharmony_ci	}
38488c2ecf20Sopenharmony_ci
38498c2ecf20Sopenharmony_ci	nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
38508c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
38518c2ecf20Sopenharmony_ci	if (!rif)
38528c2ecf20Sopenharmony_ci		return 0;
38538c2ecf20Sopenharmony_ci
38548c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_rif_init(nh, rif);
38558c2ecf20Sopenharmony_ci	err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
38568c2ecf20Sopenharmony_ci	if (err)
38578c2ecf20Sopenharmony_ci		goto err_neigh_init;
38588c2ecf20Sopenharmony_ci
38598c2ecf20Sopenharmony_ci	return 0;
38608c2ecf20Sopenharmony_ci
38618c2ecf20Sopenharmony_cierr_neigh_init:
38628c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_rif_fini(nh);
38638c2ecf20Sopenharmony_ci	return err;
38648c2ecf20Sopenharmony_ci}
38658c2ecf20Sopenharmony_ci
38668c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop4_type_fini(struct mlxsw_sp *mlxsw_sp,
38678c2ecf20Sopenharmony_ci					struct mlxsw_sp_nexthop *nh)
38688c2ecf20Sopenharmony_ci{
38698c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
38708c2ecf20Sopenharmony_ci}
38718c2ecf20Sopenharmony_ci
38728c2ecf20Sopenharmony_cistatic int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
38738c2ecf20Sopenharmony_ci				  struct mlxsw_sp_nexthop_group *nh_grp,
38748c2ecf20Sopenharmony_ci				  struct mlxsw_sp_nexthop *nh,
38758c2ecf20Sopenharmony_ci				  struct fib_nh *fib_nh)
38768c2ecf20Sopenharmony_ci{
38778c2ecf20Sopenharmony_ci	struct net_device *dev = fib_nh->fib_nh_dev;
38788c2ecf20Sopenharmony_ci	struct in_device *in_dev;
38798c2ecf20Sopenharmony_ci	int err;
38808c2ecf20Sopenharmony_ci
38818c2ecf20Sopenharmony_ci	nh->nh_grp = nh_grp;
38828c2ecf20Sopenharmony_ci	nh->key.fib_nh = fib_nh;
38838c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_MULTIPATH
38848c2ecf20Sopenharmony_ci	nh->nh_weight = fib_nh->fib_nh_weight;
38858c2ecf20Sopenharmony_ci#else
38868c2ecf20Sopenharmony_ci	nh->nh_weight = 1;
38878c2ecf20Sopenharmony_ci#endif
38888c2ecf20Sopenharmony_ci	memcpy(&nh->gw_addr, &fib_nh->fib_nh_gw4, sizeof(fib_nh->fib_nh_gw4));
38898c2ecf20Sopenharmony_ci	err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
38908c2ecf20Sopenharmony_ci	if (err)
38918c2ecf20Sopenharmony_ci		return err;
38928c2ecf20Sopenharmony_ci
38938c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh);
38948c2ecf20Sopenharmony_ci	list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list);
38958c2ecf20Sopenharmony_ci
38968c2ecf20Sopenharmony_ci	if (!dev)
38978c2ecf20Sopenharmony_ci		return 0;
38988c2ecf20Sopenharmony_ci
38998c2ecf20Sopenharmony_ci	rcu_read_lock();
39008c2ecf20Sopenharmony_ci	in_dev = __in_dev_get_rcu(dev);
39018c2ecf20Sopenharmony_ci	if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
39028c2ecf20Sopenharmony_ci	    fib_nh->fib_nh_flags & RTNH_F_LINKDOWN) {
39038c2ecf20Sopenharmony_ci		rcu_read_unlock();
39048c2ecf20Sopenharmony_ci		return 0;
39058c2ecf20Sopenharmony_ci	}
39068c2ecf20Sopenharmony_ci	rcu_read_unlock();
39078c2ecf20Sopenharmony_ci
39088c2ecf20Sopenharmony_ci	err = mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
39098c2ecf20Sopenharmony_ci	if (err)
39108c2ecf20Sopenharmony_ci		goto err_nexthop_neigh_init;
39118c2ecf20Sopenharmony_ci
39128c2ecf20Sopenharmony_ci	return 0;
39138c2ecf20Sopenharmony_ci
39148c2ecf20Sopenharmony_cierr_nexthop_neigh_init:
39158c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
39168c2ecf20Sopenharmony_ci	return err;
39178c2ecf20Sopenharmony_ci}
39188c2ecf20Sopenharmony_ci
39198c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
39208c2ecf20Sopenharmony_ci				   struct mlxsw_sp_nexthop *nh)
39218c2ecf20Sopenharmony_ci{
39228c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
39238c2ecf20Sopenharmony_ci	list_del(&nh->router_list_node);
39248c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh);
39258c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
39268c2ecf20Sopenharmony_ci}
39278c2ecf20Sopenharmony_ci
39288c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
39298c2ecf20Sopenharmony_ci				    unsigned long event, struct fib_nh *fib_nh)
39308c2ecf20Sopenharmony_ci{
39318c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_key key;
39328c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop *nh;
39338c2ecf20Sopenharmony_ci
39348c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->aborted)
39358c2ecf20Sopenharmony_ci		return;
39368c2ecf20Sopenharmony_ci
39378c2ecf20Sopenharmony_ci	key.fib_nh = fib_nh;
39388c2ecf20Sopenharmony_ci	nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
39398c2ecf20Sopenharmony_ci	if (!nh)
39408c2ecf20Sopenharmony_ci		return;
39418c2ecf20Sopenharmony_ci
39428c2ecf20Sopenharmony_ci	switch (event) {
39438c2ecf20Sopenharmony_ci	case FIB_EVENT_NH_ADD:
39448c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
39458c2ecf20Sopenharmony_ci		break;
39468c2ecf20Sopenharmony_ci	case FIB_EVENT_NH_DEL:
39478c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
39488c2ecf20Sopenharmony_ci		break;
39498c2ecf20Sopenharmony_ci	}
39508c2ecf20Sopenharmony_ci
39518c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
39528c2ecf20Sopenharmony_ci}
39538c2ecf20Sopenharmony_ci
39548c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop_rif_update(struct mlxsw_sp *mlxsw_sp,
39558c2ecf20Sopenharmony_ci					struct mlxsw_sp_rif *rif)
39568c2ecf20Sopenharmony_ci{
39578c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop *nh;
39588c2ecf20Sopenharmony_ci	bool removing;
39598c2ecf20Sopenharmony_ci
39608c2ecf20Sopenharmony_ci	list_for_each_entry(nh, &rif->nexthop_list, rif_list_node) {
39618c2ecf20Sopenharmony_ci		switch (nh->type) {
39628c2ecf20Sopenharmony_ci		case MLXSW_SP_NEXTHOP_TYPE_ETH:
39638c2ecf20Sopenharmony_ci			removing = false;
39648c2ecf20Sopenharmony_ci			break;
39658c2ecf20Sopenharmony_ci		case MLXSW_SP_NEXTHOP_TYPE_IPIP:
39668c2ecf20Sopenharmony_ci			removing = !mlxsw_sp_ipip_netdev_ul_up(rif->dev);
39678c2ecf20Sopenharmony_ci			break;
39688c2ecf20Sopenharmony_ci		default:
39698c2ecf20Sopenharmony_ci			WARN_ON(1);
39708c2ecf20Sopenharmony_ci			continue;
39718c2ecf20Sopenharmony_ci		}
39728c2ecf20Sopenharmony_ci
39738c2ecf20Sopenharmony_ci		__mlxsw_sp_nexthop_neigh_update(nh, removing);
39748c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
39758c2ecf20Sopenharmony_ci	}
39768c2ecf20Sopenharmony_ci}
39778c2ecf20Sopenharmony_ci
39788c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop_rif_migrate(struct mlxsw_sp *mlxsw_sp,
39798c2ecf20Sopenharmony_ci					 struct mlxsw_sp_rif *old_rif,
39808c2ecf20Sopenharmony_ci					 struct mlxsw_sp_rif *new_rif)
39818c2ecf20Sopenharmony_ci{
39828c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop *nh;
39838c2ecf20Sopenharmony_ci
39848c2ecf20Sopenharmony_ci	list_splice_init(&old_rif->nexthop_list, &new_rif->nexthop_list);
39858c2ecf20Sopenharmony_ci	list_for_each_entry(nh, &new_rif->nexthop_list, rif_list_node)
39868c2ecf20Sopenharmony_ci		nh->rif = new_rif;
39878c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_rif_update(mlxsw_sp, new_rif);
39888c2ecf20Sopenharmony_ci}
39898c2ecf20Sopenharmony_ci
39908c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
39918c2ecf20Sopenharmony_ci					   struct mlxsw_sp_rif *rif)
39928c2ecf20Sopenharmony_ci{
39938c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop *nh, *tmp;
39948c2ecf20Sopenharmony_ci
39958c2ecf20Sopenharmony_ci	list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
39968c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
39978c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
39988c2ecf20Sopenharmony_ci	}
39998c2ecf20Sopenharmony_ci}
40008c2ecf20Sopenharmony_ci
40018c2ecf20Sopenharmony_cistatic bool mlxsw_sp_fi_is_gateway(const struct mlxsw_sp *mlxsw_sp,
40028c2ecf20Sopenharmony_ci				   struct fib_info *fi)
40038c2ecf20Sopenharmony_ci{
40048c2ecf20Sopenharmony_ci	const struct fib_nh *nh = fib_info_nh(fi, 0);
40058c2ecf20Sopenharmony_ci
40068c2ecf20Sopenharmony_ci	return nh->fib_nh_gw_family ||
40078c2ecf20Sopenharmony_ci	       mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, nh, NULL);
40088c2ecf20Sopenharmony_ci}
40098c2ecf20Sopenharmony_ci
40108c2ecf20Sopenharmony_cistatic struct mlxsw_sp_nexthop_group *
40118c2ecf20Sopenharmony_cimlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
40128c2ecf20Sopenharmony_ci{
40138c2ecf20Sopenharmony_ci	unsigned int nhs = fib_info_num_path(fi);
40148c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group *nh_grp;
40158c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop *nh;
40168c2ecf20Sopenharmony_ci	struct fib_nh *fib_nh;
40178c2ecf20Sopenharmony_ci	int i;
40188c2ecf20Sopenharmony_ci	int err;
40198c2ecf20Sopenharmony_ci
40208c2ecf20Sopenharmony_ci	nh_grp = kzalloc(struct_size(nh_grp, nexthops, nhs), GFP_KERNEL);
40218c2ecf20Sopenharmony_ci	if (!nh_grp)
40228c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
40238c2ecf20Sopenharmony_ci	nh_grp->priv = fi;
40248c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&nh_grp->fib_list);
40258c2ecf20Sopenharmony_ci	nh_grp->neigh_tbl = &arp_tbl;
40268c2ecf20Sopenharmony_ci
40278c2ecf20Sopenharmony_ci	nh_grp->gateway = mlxsw_sp_fi_is_gateway(mlxsw_sp, fi);
40288c2ecf20Sopenharmony_ci	nh_grp->count = nhs;
40298c2ecf20Sopenharmony_ci	fib_info_hold(fi);
40308c2ecf20Sopenharmony_ci	for (i = 0; i < nh_grp->count; i++) {
40318c2ecf20Sopenharmony_ci		nh = &nh_grp->nexthops[i];
40328c2ecf20Sopenharmony_ci		fib_nh = fib_info_nh(fi, i);
40338c2ecf20Sopenharmony_ci		err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
40348c2ecf20Sopenharmony_ci		if (err)
40358c2ecf20Sopenharmony_ci			goto err_nexthop4_init;
40368c2ecf20Sopenharmony_ci	}
40378c2ecf20Sopenharmony_ci	err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
40388c2ecf20Sopenharmony_ci	if (err)
40398c2ecf20Sopenharmony_ci		goto err_nexthop_group_insert;
40408c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
40418c2ecf20Sopenharmony_ci	return nh_grp;
40428c2ecf20Sopenharmony_ci
40438c2ecf20Sopenharmony_cierr_nexthop_group_insert:
40448c2ecf20Sopenharmony_cierr_nexthop4_init:
40458c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--) {
40468c2ecf20Sopenharmony_ci		nh = &nh_grp->nexthops[i];
40478c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
40488c2ecf20Sopenharmony_ci	}
40498c2ecf20Sopenharmony_ci	fib_info_put(fi);
40508c2ecf20Sopenharmony_ci	kfree(nh_grp);
40518c2ecf20Sopenharmony_ci	return ERR_PTR(err);
40528c2ecf20Sopenharmony_ci}
40538c2ecf20Sopenharmony_ci
40548c2ecf20Sopenharmony_cistatic void
40558c2ecf20Sopenharmony_cimlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
40568c2ecf20Sopenharmony_ci				struct mlxsw_sp_nexthop_group *nh_grp)
40578c2ecf20Sopenharmony_ci{
40588c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop *nh;
40598c2ecf20Sopenharmony_ci	int i;
40608c2ecf20Sopenharmony_ci
40618c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
40628c2ecf20Sopenharmony_ci	for (i = 0; i < nh_grp->count; i++) {
40638c2ecf20Sopenharmony_ci		nh = &nh_grp->nexthops[i];
40648c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
40658c2ecf20Sopenharmony_ci	}
40668c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
40678c2ecf20Sopenharmony_ci	WARN_ON_ONCE(nh_grp->adj_index_valid);
40688c2ecf20Sopenharmony_ci	fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
40698c2ecf20Sopenharmony_ci	kfree(nh_grp);
40708c2ecf20Sopenharmony_ci}
40718c2ecf20Sopenharmony_ci
40728c2ecf20Sopenharmony_cistatic int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
40738c2ecf20Sopenharmony_ci				       struct mlxsw_sp_fib_entry *fib_entry,
40748c2ecf20Sopenharmony_ci				       struct fib_info *fi)
40758c2ecf20Sopenharmony_ci{
40768c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group *nh_grp;
40778c2ecf20Sopenharmony_ci
40788c2ecf20Sopenharmony_ci	nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);
40798c2ecf20Sopenharmony_ci	if (!nh_grp) {
40808c2ecf20Sopenharmony_ci		nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
40818c2ecf20Sopenharmony_ci		if (IS_ERR(nh_grp))
40828c2ecf20Sopenharmony_ci			return PTR_ERR(nh_grp);
40838c2ecf20Sopenharmony_ci	}
40848c2ecf20Sopenharmony_ci	list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
40858c2ecf20Sopenharmony_ci	fib_entry->nh_group = nh_grp;
40868c2ecf20Sopenharmony_ci	return 0;
40878c2ecf20Sopenharmony_ci}
40888c2ecf20Sopenharmony_ci
40898c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
40908c2ecf20Sopenharmony_ci					struct mlxsw_sp_fib_entry *fib_entry)
40918c2ecf20Sopenharmony_ci{
40928c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
40938c2ecf20Sopenharmony_ci
40948c2ecf20Sopenharmony_ci	list_del(&fib_entry->nexthop_group_node);
40958c2ecf20Sopenharmony_ci	if (!list_empty(&nh_grp->fib_list))
40968c2ecf20Sopenharmony_ci		return;
40978c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
40988c2ecf20Sopenharmony_ci}
40998c2ecf20Sopenharmony_ci
41008c2ecf20Sopenharmony_cistatic bool
41018c2ecf20Sopenharmony_cimlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
41028c2ecf20Sopenharmony_ci{
41038c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib4_entry *fib4_entry;
41048c2ecf20Sopenharmony_ci
41058c2ecf20Sopenharmony_ci	fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
41068c2ecf20Sopenharmony_ci				  common);
41078c2ecf20Sopenharmony_ci	return !fib4_entry->tos;
41088c2ecf20Sopenharmony_ci}
41098c2ecf20Sopenharmony_ci
41108c2ecf20Sopenharmony_cistatic bool
41118c2ecf20Sopenharmony_cimlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
41128c2ecf20Sopenharmony_ci{
41138c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
41148c2ecf20Sopenharmony_ci
41158c2ecf20Sopenharmony_ci	switch (fib_entry->fib_node->fib->proto) {
41168c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
41178c2ecf20Sopenharmony_ci		if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
41188c2ecf20Sopenharmony_ci			return false;
41198c2ecf20Sopenharmony_ci		break;
41208c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
41218c2ecf20Sopenharmony_ci		break;
41228c2ecf20Sopenharmony_ci	}
41238c2ecf20Sopenharmony_ci
41248c2ecf20Sopenharmony_ci	switch (fib_entry->type) {
41258c2ecf20Sopenharmony_ci	case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
41268c2ecf20Sopenharmony_ci		return !!nh_group->adj_index_valid;
41278c2ecf20Sopenharmony_ci	case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
41288c2ecf20Sopenharmony_ci		return !!nh_group->nh_rif;
41298c2ecf20Sopenharmony_ci	case MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE:
41308c2ecf20Sopenharmony_ci	case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
41318c2ecf20Sopenharmony_ci	case MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP:
41328c2ecf20Sopenharmony_ci		return true;
41338c2ecf20Sopenharmony_ci	default:
41348c2ecf20Sopenharmony_ci		return false;
41358c2ecf20Sopenharmony_ci	}
41368c2ecf20Sopenharmony_ci}
41378c2ecf20Sopenharmony_ci
41388c2ecf20Sopenharmony_cistatic struct mlxsw_sp_nexthop *
41398c2ecf20Sopenharmony_cimlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
41408c2ecf20Sopenharmony_ci		     const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
41418c2ecf20Sopenharmony_ci{
41428c2ecf20Sopenharmony_ci	int i;
41438c2ecf20Sopenharmony_ci
41448c2ecf20Sopenharmony_ci	for (i = 0; i < nh_grp->count; i++) {
41458c2ecf20Sopenharmony_ci		struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
41468c2ecf20Sopenharmony_ci		struct fib6_info *rt = mlxsw_sp_rt6->rt;
41478c2ecf20Sopenharmony_ci
41488c2ecf20Sopenharmony_ci		if (nh->rif && nh->rif->dev == rt->fib6_nh->fib_nh_dev &&
41498c2ecf20Sopenharmony_ci		    ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
41508c2ecf20Sopenharmony_ci				    &rt->fib6_nh->fib_nh_gw6))
41518c2ecf20Sopenharmony_ci			return nh;
41528c2ecf20Sopenharmony_ci		continue;
41538c2ecf20Sopenharmony_ci	}
41548c2ecf20Sopenharmony_ci
41558c2ecf20Sopenharmony_ci	return NULL;
41568c2ecf20Sopenharmony_ci}
41578c2ecf20Sopenharmony_ci
41588c2ecf20Sopenharmony_cistatic void
41598c2ecf20Sopenharmony_cimlxsw_sp_fib4_entry_hw_flags_set(struct mlxsw_sp *mlxsw_sp,
41608c2ecf20Sopenharmony_ci				 struct mlxsw_sp_fib_entry *fib_entry)
41618c2ecf20Sopenharmony_ci{
41628c2ecf20Sopenharmony_ci	struct fib_info *fi = mlxsw_sp_nexthop4_group_fi(fib_entry->nh_group);
41638c2ecf20Sopenharmony_ci	u32 *p_dst = (u32 *) fib_entry->fib_node->key.addr;
41648c2ecf20Sopenharmony_ci	int dst_len = fib_entry->fib_node->key.prefix_len;
41658c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib4_entry *fib4_entry;
41668c2ecf20Sopenharmony_ci	struct fib_rt_info fri;
41678c2ecf20Sopenharmony_ci	bool should_offload;
41688c2ecf20Sopenharmony_ci
41698c2ecf20Sopenharmony_ci	should_offload = mlxsw_sp_fib_entry_should_offload(fib_entry);
41708c2ecf20Sopenharmony_ci	fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
41718c2ecf20Sopenharmony_ci				  common);
41728c2ecf20Sopenharmony_ci	fri.fi = fi;
41738c2ecf20Sopenharmony_ci	fri.tb_id = fib4_entry->tb_id;
41748c2ecf20Sopenharmony_ci	fri.dst = cpu_to_be32(*p_dst);
41758c2ecf20Sopenharmony_ci	fri.dst_len = dst_len;
41768c2ecf20Sopenharmony_ci	fri.tos = fib4_entry->tos;
41778c2ecf20Sopenharmony_ci	fri.type = fib4_entry->type;
41788c2ecf20Sopenharmony_ci	fri.offload = should_offload;
41798c2ecf20Sopenharmony_ci	fri.trap = !should_offload;
41808c2ecf20Sopenharmony_ci	fib_alias_hw_flags_set(mlxsw_sp_net(mlxsw_sp), &fri);
41818c2ecf20Sopenharmony_ci}
41828c2ecf20Sopenharmony_ci
41838c2ecf20Sopenharmony_cistatic void
41848c2ecf20Sopenharmony_cimlxsw_sp_fib4_entry_hw_flags_clear(struct mlxsw_sp *mlxsw_sp,
41858c2ecf20Sopenharmony_ci				   struct mlxsw_sp_fib_entry *fib_entry)
41868c2ecf20Sopenharmony_ci{
41878c2ecf20Sopenharmony_ci	struct fib_info *fi = mlxsw_sp_nexthop4_group_fi(fib_entry->nh_group);
41888c2ecf20Sopenharmony_ci	u32 *p_dst = (u32 *) fib_entry->fib_node->key.addr;
41898c2ecf20Sopenharmony_ci	int dst_len = fib_entry->fib_node->key.prefix_len;
41908c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib4_entry *fib4_entry;
41918c2ecf20Sopenharmony_ci	struct fib_rt_info fri;
41928c2ecf20Sopenharmony_ci
41938c2ecf20Sopenharmony_ci	fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
41948c2ecf20Sopenharmony_ci				  common);
41958c2ecf20Sopenharmony_ci	fri.fi = fi;
41968c2ecf20Sopenharmony_ci	fri.tb_id = fib4_entry->tb_id;
41978c2ecf20Sopenharmony_ci	fri.dst = cpu_to_be32(*p_dst);
41988c2ecf20Sopenharmony_ci	fri.dst_len = dst_len;
41998c2ecf20Sopenharmony_ci	fri.tos = fib4_entry->tos;
42008c2ecf20Sopenharmony_ci	fri.type = fib4_entry->type;
42018c2ecf20Sopenharmony_ci	fri.offload = false;
42028c2ecf20Sopenharmony_ci	fri.trap = false;
42038c2ecf20Sopenharmony_ci	fib_alias_hw_flags_set(mlxsw_sp_net(mlxsw_sp), &fri);
42048c2ecf20Sopenharmony_ci}
42058c2ecf20Sopenharmony_ci
42068c2ecf20Sopenharmony_cistatic void
42078c2ecf20Sopenharmony_cimlxsw_sp_fib6_entry_hw_flags_set(struct mlxsw_sp *mlxsw_sp,
42088c2ecf20Sopenharmony_ci				 struct mlxsw_sp_fib_entry *fib_entry)
42098c2ecf20Sopenharmony_ci{
42108c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib6_entry *fib6_entry;
42118c2ecf20Sopenharmony_ci	struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
42128c2ecf20Sopenharmony_ci	bool should_offload;
42138c2ecf20Sopenharmony_ci
42148c2ecf20Sopenharmony_ci	should_offload = mlxsw_sp_fib_entry_should_offload(fib_entry);
42158c2ecf20Sopenharmony_ci
42168c2ecf20Sopenharmony_ci	/* In IPv6 a multipath route is represented using multiple routes, so
42178c2ecf20Sopenharmony_ci	 * we need to set the flags on all of them.
42188c2ecf20Sopenharmony_ci	 */
42198c2ecf20Sopenharmony_ci	fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
42208c2ecf20Sopenharmony_ci				  common);
42218c2ecf20Sopenharmony_ci	list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list)
42228c2ecf20Sopenharmony_ci		fib6_info_hw_flags_set(mlxsw_sp_rt6->rt, should_offload,
42238c2ecf20Sopenharmony_ci				       !should_offload);
42248c2ecf20Sopenharmony_ci}
42258c2ecf20Sopenharmony_ci
42268c2ecf20Sopenharmony_cistatic void
42278c2ecf20Sopenharmony_cimlxsw_sp_fib6_entry_hw_flags_clear(struct mlxsw_sp *mlxsw_sp,
42288c2ecf20Sopenharmony_ci				   struct mlxsw_sp_fib_entry *fib_entry)
42298c2ecf20Sopenharmony_ci{
42308c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib6_entry *fib6_entry;
42318c2ecf20Sopenharmony_ci	struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
42328c2ecf20Sopenharmony_ci
42338c2ecf20Sopenharmony_ci	fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
42348c2ecf20Sopenharmony_ci				  common);
42358c2ecf20Sopenharmony_ci	list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list)
42368c2ecf20Sopenharmony_ci		fib6_info_hw_flags_set(mlxsw_sp_rt6->rt, false, false);
42378c2ecf20Sopenharmony_ci}
42388c2ecf20Sopenharmony_ci
42398c2ecf20Sopenharmony_cistatic void
42408c2ecf20Sopenharmony_cimlxsw_sp_fib_entry_hw_flags_set(struct mlxsw_sp *mlxsw_sp,
42418c2ecf20Sopenharmony_ci				struct mlxsw_sp_fib_entry *fib_entry)
42428c2ecf20Sopenharmony_ci{
42438c2ecf20Sopenharmony_ci	switch (fib_entry->fib_node->fib->proto) {
42448c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
42458c2ecf20Sopenharmony_ci		mlxsw_sp_fib4_entry_hw_flags_set(mlxsw_sp, fib_entry);
42468c2ecf20Sopenharmony_ci		break;
42478c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
42488c2ecf20Sopenharmony_ci		mlxsw_sp_fib6_entry_hw_flags_set(mlxsw_sp, fib_entry);
42498c2ecf20Sopenharmony_ci		break;
42508c2ecf20Sopenharmony_ci	}
42518c2ecf20Sopenharmony_ci}
42528c2ecf20Sopenharmony_ci
42538c2ecf20Sopenharmony_cistatic void
42548c2ecf20Sopenharmony_cimlxsw_sp_fib_entry_hw_flags_clear(struct mlxsw_sp *mlxsw_sp,
42558c2ecf20Sopenharmony_ci				  struct mlxsw_sp_fib_entry *fib_entry)
42568c2ecf20Sopenharmony_ci{
42578c2ecf20Sopenharmony_ci	switch (fib_entry->fib_node->fib->proto) {
42588c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
42598c2ecf20Sopenharmony_ci		mlxsw_sp_fib4_entry_hw_flags_clear(mlxsw_sp, fib_entry);
42608c2ecf20Sopenharmony_ci		break;
42618c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
42628c2ecf20Sopenharmony_ci		mlxsw_sp_fib6_entry_hw_flags_clear(mlxsw_sp, fib_entry);
42638c2ecf20Sopenharmony_ci		break;
42648c2ecf20Sopenharmony_ci	}
42658c2ecf20Sopenharmony_ci}
42668c2ecf20Sopenharmony_ci
42678c2ecf20Sopenharmony_cistatic void
42688c2ecf20Sopenharmony_cimlxsw_sp_fib_entry_hw_flags_refresh(struct mlxsw_sp *mlxsw_sp,
42698c2ecf20Sopenharmony_ci				    struct mlxsw_sp_fib_entry *fib_entry,
42708c2ecf20Sopenharmony_ci				    enum mlxsw_reg_ralue_op op)
42718c2ecf20Sopenharmony_ci{
42728c2ecf20Sopenharmony_ci	switch (op) {
42738c2ecf20Sopenharmony_ci	case MLXSW_REG_RALUE_OP_WRITE_WRITE:
42748c2ecf20Sopenharmony_ci		mlxsw_sp_fib_entry_hw_flags_set(mlxsw_sp, fib_entry);
42758c2ecf20Sopenharmony_ci		break;
42768c2ecf20Sopenharmony_ci	case MLXSW_REG_RALUE_OP_WRITE_DELETE:
42778c2ecf20Sopenharmony_ci		mlxsw_sp_fib_entry_hw_flags_clear(mlxsw_sp, fib_entry);
42788c2ecf20Sopenharmony_ci		break;
42798c2ecf20Sopenharmony_ci	default:
42808c2ecf20Sopenharmony_ci		break;
42818c2ecf20Sopenharmony_ci	}
42828c2ecf20Sopenharmony_ci}
42838c2ecf20Sopenharmony_ci
42848c2ecf20Sopenharmony_cistatic void
42858c2ecf20Sopenharmony_cimlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
42868c2ecf20Sopenharmony_ci			      const struct mlxsw_sp_fib_entry *fib_entry,
42878c2ecf20Sopenharmony_ci			      enum mlxsw_reg_ralue_op op)
42888c2ecf20Sopenharmony_ci{
42898c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
42908c2ecf20Sopenharmony_ci	enum mlxsw_reg_ralxx_protocol proto;
42918c2ecf20Sopenharmony_ci	u32 *p_dip;
42928c2ecf20Sopenharmony_ci
42938c2ecf20Sopenharmony_ci	proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
42948c2ecf20Sopenharmony_ci
42958c2ecf20Sopenharmony_ci	switch (fib->proto) {
42968c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
42978c2ecf20Sopenharmony_ci		p_dip = (u32 *) fib_entry->fib_node->key.addr;
42988c2ecf20Sopenharmony_ci		mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
42998c2ecf20Sopenharmony_ci				      fib_entry->fib_node->key.prefix_len,
43008c2ecf20Sopenharmony_ci				      *p_dip);
43018c2ecf20Sopenharmony_ci		break;
43028c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
43038c2ecf20Sopenharmony_ci		mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
43048c2ecf20Sopenharmony_ci				      fib_entry->fib_node->key.prefix_len,
43058c2ecf20Sopenharmony_ci				      fib_entry->fib_node->key.addr);
43068c2ecf20Sopenharmony_ci		break;
43078c2ecf20Sopenharmony_ci	}
43088c2ecf20Sopenharmony_ci}
43098c2ecf20Sopenharmony_ci
43108c2ecf20Sopenharmony_cistatic int mlxsw_sp_adj_discard_write(struct mlxsw_sp *mlxsw_sp, u16 rif_index)
43118c2ecf20Sopenharmony_ci{
43128c2ecf20Sopenharmony_ci	enum mlxsw_reg_ratr_trap_action trap_action;
43138c2ecf20Sopenharmony_ci	char ratr_pl[MLXSW_REG_RATR_LEN];
43148c2ecf20Sopenharmony_ci	int err;
43158c2ecf20Sopenharmony_ci
43168c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->adj_discard_index_valid)
43178c2ecf20Sopenharmony_ci		return 0;
43188c2ecf20Sopenharmony_ci
43198c2ecf20Sopenharmony_ci	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
43208c2ecf20Sopenharmony_ci				  &mlxsw_sp->router->adj_discard_index);
43218c2ecf20Sopenharmony_ci	if (err)
43228c2ecf20Sopenharmony_ci		return err;
43238c2ecf20Sopenharmony_ci
43248c2ecf20Sopenharmony_ci	trap_action = MLXSW_REG_RATR_TRAP_ACTION_DISCARD_ERRORS;
43258c2ecf20Sopenharmony_ci	mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY, true,
43268c2ecf20Sopenharmony_ci			    MLXSW_REG_RATR_TYPE_ETHERNET,
43278c2ecf20Sopenharmony_ci			    mlxsw_sp->router->adj_discard_index, rif_index);
43288c2ecf20Sopenharmony_ci	mlxsw_reg_ratr_trap_action_set(ratr_pl, trap_action);
43298c2ecf20Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
43308c2ecf20Sopenharmony_ci	if (err)
43318c2ecf20Sopenharmony_ci		goto err_ratr_write;
43328c2ecf20Sopenharmony_ci
43338c2ecf20Sopenharmony_ci	mlxsw_sp->router->adj_discard_index_valid = true;
43348c2ecf20Sopenharmony_ci
43358c2ecf20Sopenharmony_ci	return 0;
43368c2ecf20Sopenharmony_ci
43378c2ecf20Sopenharmony_cierr_ratr_write:
43388c2ecf20Sopenharmony_ci	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
43398c2ecf20Sopenharmony_ci			   mlxsw_sp->router->adj_discard_index);
43408c2ecf20Sopenharmony_ci	return err;
43418c2ecf20Sopenharmony_ci}
43428c2ecf20Sopenharmony_ci
43438c2ecf20Sopenharmony_cistatic int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
43448c2ecf20Sopenharmony_ci					struct mlxsw_sp_fib_entry *fib_entry,
43458c2ecf20Sopenharmony_ci					enum mlxsw_reg_ralue_op op)
43468c2ecf20Sopenharmony_ci{
43478c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
43488c2ecf20Sopenharmony_ci	char ralue_pl[MLXSW_REG_RALUE_LEN];
43498c2ecf20Sopenharmony_ci	enum mlxsw_reg_ralue_trap_action trap_action;
43508c2ecf20Sopenharmony_ci	u16 trap_id = 0;
43518c2ecf20Sopenharmony_ci	u32 adjacency_index = 0;
43528c2ecf20Sopenharmony_ci	u16 ecmp_size = 0;
43538c2ecf20Sopenharmony_ci	int err;
43548c2ecf20Sopenharmony_ci
43558c2ecf20Sopenharmony_ci	/* In case the nexthop group adjacency index is valid, use it
43568c2ecf20Sopenharmony_ci	 * with provided ECMP size. Otherwise, setup trap and pass
43578c2ecf20Sopenharmony_ci	 * traffic to kernel.
43588c2ecf20Sopenharmony_ci	 */
43598c2ecf20Sopenharmony_ci	if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
43608c2ecf20Sopenharmony_ci		trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
43618c2ecf20Sopenharmony_ci		adjacency_index = fib_entry->nh_group->adj_index;
43628c2ecf20Sopenharmony_ci		ecmp_size = fib_entry->nh_group->ecmp_size;
43638c2ecf20Sopenharmony_ci	} else if (!nh_group->adj_index_valid && nh_group->count &&
43648c2ecf20Sopenharmony_ci		   nh_group->nh_rif) {
43658c2ecf20Sopenharmony_ci		err = mlxsw_sp_adj_discard_write(mlxsw_sp,
43668c2ecf20Sopenharmony_ci						 nh_group->nh_rif->rif_index);
43678c2ecf20Sopenharmony_ci		if (err)
43688c2ecf20Sopenharmony_ci			return err;
43698c2ecf20Sopenharmony_ci		trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
43708c2ecf20Sopenharmony_ci		adjacency_index = mlxsw_sp->router->adj_discard_index;
43718c2ecf20Sopenharmony_ci		ecmp_size = 1;
43728c2ecf20Sopenharmony_ci	} else {
43738c2ecf20Sopenharmony_ci		trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
43748c2ecf20Sopenharmony_ci		trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
43758c2ecf20Sopenharmony_ci	}
43768c2ecf20Sopenharmony_ci
43778c2ecf20Sopenharmony_ci	mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
43788c2ecf20Sopenharmony_ci	mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
43798c2ecf20Sopenharmony_ci					adjacency_index, ecmp_size);
43808c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
43818c2ecf20Sopenharmony_ci}
43828c2ecf20Sopenharmony_ci
43838c2ecf20Sopenharmony_cistatic int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
43848c2ecf20Sopenharmony_ci				       struct mlxsw_sp_fib_entry *fib_entry,
43858c2ecf20Sopenharmony_ci				       enum mlxsw_reg_ralue_op op)
43868c2ecf20Sopenharmony_ci{
43878c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
43888c2ecf20Sopenharmony_ci	enum mlxsw_reg_ralue_trap_action trap_action;
43898c2ecf20Sopenharmony_ci	char ralue_pl[MLXSW_REG_RALUE_LEN];
43908c2ecf20Sopenharmony_ci	u16 trap_id = 0;
43918c2ecf20Sopenharmony_ci	u16 rif_index = 0;
43928c2ecf20Sopenharmony_ci
43938c2ecf20Sopenharmony_ci	if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
43948c2ecf20Sopenharmony_ci		trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
43958c2ecf20Sopenharmony_ci		rif_index = rif->rif_index;
43968c2ecf20Sopenharmony_ci	} else {
43978c2ecf20Sopenharmony_ci		trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
43988c2ecf20Sopenharmony_ci		trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
43998c2ecf20Sopenharmony_ci	}
44008c2ecf20Sopenharmony_ci
44018c2ecf20Sopenharmony_ci	mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
44028c2ecf20Sopenharmony_ci	mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
44038c2ecf20Sopenharmony_ci				       rif_index);
44048c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
44058c2ecf20Sopenharmony_ci}
44068c2ecf20Sopenharmony_ci
44078c2ecf20Sopenharmony_cistatic int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
44088c2ecf20Sopenharmony_ci				      struct mlxsw_sp_fib_entry *fib_entry,
44098c2ecf20Sopenharmony_ci				      enum mlxsw_reg_ralue_op op)
44108c2ecf20Sopenharmony_ci{
44118c2ecf20Sopenharmony_ci	char ralue_pl[MLXSW_REG_RALUE_LEN];
44128c2ecf20Sopenharmony_ci
44138c2ecf20Sopenharmony_ci	mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
44148c2ecf20Sopenharmony_ci	mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
44158c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
44168c2ecf20Sopenharmony_ci}
44178c2ecf20Sopenharmony_ci
44188c2ecf20Sopenharmony_cistatic int mlxsw_sp_fib_entry_op_blackhole(struct mlxsw_sp *mlxsw_sp,
44198c2ecf20Sopenharmony_ci					   struct mlxsw_sp_fib_entry *fib_entry,
44208c2ecf20Sopenharmony_ci					   enum mlxsw_reg_ralue_op op)
44218c2ecf20Sopenharmony_ci{
44228c2ecf20Sopenharmony_ci	enum mlxsw_reg_ralue_trap_action trap_action;
44238c2ecf20Sopenharmony_ci	char ralue_pl[MLXSW_REG_RALUE_LEN];
44248c2ecf20Sopenharmony_ci
44258c2ecf20Sopenharmony_ci	trap_action = MLXSW_REG_RALUE_TRAP_ACTION_DISCARD_ERROR;
44268c2ecf20Sopenharmony_ci	mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
44278c2ecf20Sopenharmony_ci	mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, 0, 0);
44288c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
44298c2ecf20Sopenharmony_ci}
44308c2ecf20Sopenharmony_ci
44318c2ecf20Sopenharmony_cistatic int
44328c2ecf20Sopenharmony_cimlxsw_sp_fib_entry_op_unreachable(struct mlxsw_sp *mlxsw_sp,
44338c2ecf20Sopenharmony_ci				  struct mlxsw_sp_fib_entry *fib_entry,
44348c2ecf20Sopenharmony_ci				  enum mlxsw_reg_ralue_op op)
44358c2ecf20Sopenharmony_ci{
44368c2ecf20Sopenharmony_ci	enum mlxsw_reg_ralue_trap_action trap_action;
44378c2ecf20Sopenharmony_ci	char ralue_pl[MLXSW_REG_RALUE_LEN];
44388c2ecf20Sopenharmony_ci	u16 trap_id;
44398c2ecf20Sopenharmony_ci
44408c2ecf20Sopenharmony_ci	trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
44418c2ecf20Sopenharmony_ci	trap_id = MLXSW_TRAP_ID_RTR_INGRESS1;
44428c2ecf20Sopenharmony_ci
44438c2ecf20Sopenharmony_ci	mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
44448c2ecf20Sopenharmony_ci	mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id, 0);
44458c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
44468c2ecf20Sopenharmony_ci}
44478c2ecf20Sopenharmony_ci
44488c2ecf20Sopenharmony_cistatic int
44498c2ecf20Sopenharmony_cimlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp,
44508c2ecf20Sopenharmony_ci				 struct mlxsw_sp_fib_entry *fib_entry,
44518c2ecf20Sopenharmony_ci				 enum mlxsw_reg_ralue_op op)
44528c2ecf20Sopenharmony_ci{
44538c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry = fib_entry->decap.ipip_entry;
44548c2ecf20Sopenharmony_ci	const struct mlxsw_sp_ipip_ops *ipip_ops;
44558c2ecf20Sopenharmony_ci
44568c2ecf20Sopenharmony_ci	if (WARN_ON(!ipip_entry))
44578c2ecf20Sopenharmony_ci		return -EINVAL;
44588c2ecf20Sopenharmony_ci
44598c2ecf20Sopenharmony_ci	ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
44608c2ecf20Sopenharmony_ci	return ipip_ops->fib_entry_op(mlxsw_sp, ipip_entry, op,
44618c2ecf20Sopenharmony_ci				      fib_entry->decap.tunnel_index);
44628c2ecf20Sopenharmony_ci}
44638c2ecf20Sopenharmony_ci
44648c2ecf20Sopenharmony_cistatic int mlxsw_sp_fib_entry_op_nve_decap(struct mlxsw_sp *mlxsw_sp,
44658c2ecf20Sopenharmony_ci					   struct mlxsw_sp_fib_entry *fib_entry,
44668c2ecf20Sopenharmony_ci					   enum mlxsw_reg_ralue_op op)
44678c2ecf20Sopenharmony_ci{
44688c2ecf20Sopenharmony_ci	char ralue_pl[MLXSW_REG_RALUE_LEN];
44698c2ecf20Sopenharmony_ci
44708c2ecf20Sopenharmony_ci	mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
44718c2ecf20Sopenharmony_ci	mlxsw_reg_ralue_act_ip2me_tun_pack(ralue_pl,
44728c2ecf20Sopenharmony_ci					   fib_entry->decap.tunnel_index);
44738c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
44748c2ecf20Sopenharmony_ci}
44758c2ecf20Sopenharmony_ci
44768c2ecf20Sopenharmony_cistatic int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
44778c2ecf20Sopenharmony_ci				   struct mlxsw_sp_fib_entry *fib_entry,
44788c2ecf20Sopenharmony_ci				   enum mlxsw_reg_ralue_op op)
44798c2ecf20Sopenharmony_ci{
44808c2ecf20Sopenharmony_ci	switch (fib_entry->type) {
44818c2ecf20Sopenharmony_ci	case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
44828c2ecf20Sopenharmony_ci		return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
44838c2ecf20Sopenharmony_ci	case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
44848c2ecf20Sopenharmony_ci		return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
44858c2ecf20Sopenharmony_ci	case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
44868c2ecf20Sopenharmony_ci		return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
44878c2ecf20Sopenharmony_ci	case MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE:
44888c2ecf20Sopenharmony_ci		return mlxsw_sp_fib_entry_op_blackhole(mlxsw_sp, fib_entry, op);
44898c2ecf20Sopenharmony_ci	case MLXSW_SP_FIB_ENTRY_TYPE_UNREACHABLE:
44908c2ecf20Sopenharmony_ci		return mlxsw_sp_fib_entry_op_unreachable(mlxsw_sp, fib_entry,
44918c2ecf20Sopenharmony_ci							 op);
44928c2ecf20Sopenharmony_ci	case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
44938c2ecf20Sopenharmony_ci		return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp,
44948c2ecf20Sopenharmony_ci							fib_entry, op);
44958c2ecf20Sopenharmony_ci	case MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP:
44968c2ecf20Sopenharmony_ci		return mlxsw_sp_fib_entry_op_nve_decap(mlxsw_sp, fib_entry, op);
44978c2ecf20Sopenharmony_ci	}
44988c2ecf20Sopenharmony_ci	return -EINVAL;
44998c2ecf20Sopenharmony_ci}
45008c2ecf20Sopenharmony_ci
45018c2ecf20Sopenharmony_cistatic int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
45028c2ecf20Sopenharmony_ci				 struct mlxsw_sp_fib_entry *fib_entry,
45038c2ecf20Sopenharmony_ci				 enum mlxsw_reg_ralue_op op)
45048c2ecf20Sopenharmony_ci{
45058c2ecf20Sopenharmony_ci	int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
45068c2ecf20Sopenharmony_ci
45078c2ecf20Sopenharmony_ci	if (err)
45088c2ecf20Sopenharmony_ci		return err;
45098c2ecf20Sopenharmony_ci
45108c2ecf20Sopenharmony_ci	mlxsw_sp_fib_entry_hw_flags_refresh(mlxsw_sp, fib_entry, op);
45118c2ecf20Sopenharmony_ci
45128c2ecf20Sopenharmony_ci	return err;
45138c2ecf20Sopenharmony_ci}
45148c2ecf20Sopenharmony_ci
45158c2ecf20Sopenharmony_cistatic int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
45168c2ecf20Sopenharmony_ci				     struct mlxsw_sp_fib_entry *fib_entry)
45178c2ecf20Sopenharmony_ci{
45188c2ecf20Sopenharmony_ci	return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
45198c2ecf20Sopenharmony_ci				     MLXSW_REG_RALUE_OP_WRITE_WRITE);
45208c2ecf20Sopenharmony_ci}
45218c2ecf20Sopenharmony_ci
45228c2ecf20Sopenharmony_cistatic int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
45238c2ecf20Sopenharmony_ci				  struct mlxsw_sp_fib_entry *fib_entry)
45248c2ecf20Sopenharmony_ci{
45258c2ecf20Sopenharmony_ci	return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
45268c2ecf20Sopenharmony_ci				     MLXSW_REG_RALUE_OP_WRITE_DELETE);
45278c2ecf20Sopenharmony_ci}
45288c2ecf20Sopenharmony_ci
45298c2ecf20Sopenharmony_cistatic int
45308c2ecf20Sopenharmony_cimlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
45318c2ecf20Sopenharmony_ci			     const struct fib_entry_notifier_info *fen_info,
45328c2ecf20Sopenharmony_ci			     struct mlxsw_sp_fib_entry *fib_entry)
45338c2ecf20Sopenharmony_ci{
45348c2ecf20Sopenharmony_ci	struct net_device *dev = fib_info_nh(fen_info->fi, 0)->fib_nh_dev;
45358c2ecf20Sopenharmony_ci	union mlxsw_sp_l3addr dip = { .addr4 = htonl(fen_info->dst) };
45368c2ecf20Sopenharmony_ci	struct mlxsw_sp_router *router = mlxsw_sp->router;
45378c2ecf20Sopenharmony_ci	u32 tb_id = mlxsw_sp_fix_tb_id(fen_info->tb_id);
45388c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
45398c2ecf20Sopenharmony_ci	struct fib_info *fi = fen_info->fi;
45408c2ecf20Sopenharmony_ci
45418c2ecf20Sopenharmony_ci	switch (fen_info->type) {
45428c2ecf20Sopenharmony_ci	case RTN_LOCAL:
45438c2ecf20Sopenharmony_ci		ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, dev,
45448c2ecf20Sopenharmony_ci						 MLXSW_SP_L3_PROTO_IPV4, dip);
45458c2ecf20Sopenharmony_ci		if (ipip_entry && ipip_entry->ol_dev->flags & IFF_UP) {
45468c2ecf20Sopenharmony_ci			fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
45478c2ecf20Sopenharmony_ci			return mlxsw_sp_fib_entry_decap_init(mlxsw_sp,
45488c2ecf20Sopenharmony_ci							     fib_entry,
45498c2ecf20Sopenharmony_ci							     ipip_entry);
45508c2ecf20Sopenharmony_ci		}
45518c2ecf20Sopenharmony_ci		if (mlxsw_sp_router_nve_is_decap(mlxsw_sp, tb_id,
45528c2ecf20Sopenharmony_ci						 MLXSW_SP_L3_PROTO_IPV4,
45538c2ecf20Sopenharmony_ci						 &dip)) {
45548c2ecf20Sopenharmony_ci			u32 tunnel_index;
45558c2ecf20Sopenharmony_ci
45568c2ecf20Sopenharmony_ci			tunnel_index = router->nve_decap_config.tunnel_index;
45578c2ecf20Sopenharmony_ci			fib_entry->decap.tunnel_index = tunnel_index;
45588c2ecf20Sopenharmony_ci			fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP;
45598c2ecf20Sopenharmony_ci			return 0;
45608c2ecf20Sopenharmony_ci		}
45618c2ecf20Sopenharmony_ci		fallthrough;
45628c2ecf20Sopenharmony_ci	case RTN_BROADCAST:
45638c2ecf20Sopenharmony_ci		fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
45648c2ecf20Sopenharmony_ci		return 0;
45658c2ecf20Sopenharmony_ci	case RTN_BLACKHOLE:
45668c2ecf20Sopenharmony_ci		fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE;
45678c2ecf20Sopenharmony_ci		return 0;
45688c2ecf20Sopenharmony_ci	case RTN_UNREACHABLE:
45698c2ecf20Sopenharmony_ci	case RTN_PROHIBIT:
45708c2ecf20Sopenharmony_ci		/* Packets hitting these routes need to be trapped, but
45718c2ecf20Sopenharmony_ci		 * can do so with a lower priority than packets directed
45728c2ecf20Sopenharmony_ci		 * at the host, so use action type local instead of trap.
45738c2ecf20Sopenharmony_ci		 */
45748c2ecf20Sopenharmony_ci		fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_UNREACHABLE;
45758c2ecf20Sopenharmony_ci		return 0;
45768c2ecf20Sopenharmony_ci	case RTN_UNICAST:
45778c2ecf20Sopenharmony_ci		if (mlxsw_sp_fi_is_gateway(mlxsw_sp, fi))
45788c2ecf20Sopenharmony_ci			fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
45798c2ecf20Sopenharmony_ci		else
45808c2ecf20Sopenharmony_ci			fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
45818c2ecf20Sopenharmony_ci		return 0;
45828c2ecf20Sopenharmony_ci	default:
45838c2ecf20Sopenharmony_ci		return -EINVAL;
45848c2ecf20Sopenharmony_ci	}
45858c2ecf20Sopenharmony_ci}
45868c2ecf20Sopenharmony_ci
45878c2ecf20Sopenharmony_cistatic void
45888c2ecf20Sopenharmony_cimlxsw_sp_fib4_entry_type_unset(struct mlxsw_sp *mlxsw_sp,
45898c2ecf20Sopenharmony_ci			       struct mlxsw_sp_fib_entry *fib_entry)
45908c2ecf20Sopenharmony_ci{
45918c2ecf20Sopenharmony_ci	switch (fib_entry->type) {
45928c2ecf20Sopenharmony_ci	case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
45938c2ecf20Sopenharmony_ci		mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, fib_entry);
45948c2ecf20Sopenharmony_ci		break;
45958c2ecf20Sopenharmony_ci	default:
45968c2ecf20Sopenharmony_ci		break;
45978c2ecf20Sopenharmony_ci	}
45988c2ecf20Sopenharmony_ci}
45998c2ecf20Sopenharmony_ci
46008c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fib4_entry *
46018c2ecf20Sopenharmony_cimlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
46028c2ecf20Sopenharmony_ci			   struct mlxsw_sp_fib_node *fib_node,
46038c2ecf20Sopenharmony_ci			   const struct fib_entry_notifier_info *fen_info)
46048c2ecf20Sopenharmony_ci{
46058c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib4_entry *fib4_entry;
46068c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_entry *fib_entry;
46078c2ecf20Sopenharmony_ci	int err;
46088c2ecf20Sopenharmony_ci
46098c2ecf20Sopenharmony_ci	fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
46108c2ecf20Sopenharmony_ci	if (!fib4_entry)
46118c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
46128c2ecf20Sopenharmony_ci	fib_entry = &fib4_entry->common;
46138c2ecf20Sopenharmony_ci
46148c2ecf20Sopenharmony_ci	err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
46158c2ecf20Sopenharmony_ci	if (err)
46168c2ecf20Sopenharmony_ci		goto err_fib4_entry_type_set;
46178c2ecf20Sopenharmony_ci
46188c2ecf20Sopenharmony_ci	err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
46198c2ecf20Sopenharmony_ci	if (err)
46208c2ecf20Sopenharmony_ci		goto err_nexthop4_group_get;
46218c2ecf20Sopenharmony_ci
46228c2ecf20Sopenharmony_ci	fib4_entry->prio = fen_info->fi->fib_priority;
46238c2ecf20Sopenharmony_ci	fib4_entry->tb_id = fen_info->tb_id;
46248c2ecf20Sopenharmony_ci	fib4_entry->type = fen_info->type;
46258c2ecf20Sopenharmony_ci	fib4_entry->tos = fen_info->tos;
46268c2ecf20Sopenharmony_ci
46278c2ecf20Sopenharmony_ci	fib_entry->fib_node = fib_node;
46288c2ecf20Sopenharmony_ci
46298c2ecf20Sopenharmony_ci	return fib4_entry;
46308c2ecf20Sopenharmony_ci
46318c2ecf20Sopenharmony_cierr_nexthop4_group_get:
46328c2ecf20Sopenharmony_ci	mlxsw_sp_fib4_entry_type_unset(mlxsw_sp, fib_entry);
46338c2ecf20Sopenharmony_cierr_fib4_entry_type_set:
46348c2ecf20Sopenharmony_ci	kfree(fib4_entry);
46358c2ecf20Sopenharmony_ci	return ERR_PTR(err);
46368c2ecf20Sopenharmony_ci}
46378c2ecf20Sopenharmony_ci
46388c2ecf20Sopenharmony_cistatic void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
46398c2ecf20Sopenharmony_ci					struct mlxsw_sp_fib4_entry *fib4_entry)
46408c2ecf20Sopenharmony_ci{
46418c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
46428c2ecf20Sopenharmony_ci	mlxsw_sp_fib4_entry_type_unset(mlxsw_sp, &fib4_entry->common);
46438c2ecf20Sopenharmony_ci	kfree(fib4_entry);
46448c2ecf20Sopenharmony_ci}
46458c2ecf20Sopenharmony_ci
46468c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fib4_entry *
46478c2ecf20Sopenharmony_cimlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
46488c2ecf20Sopenharmony_ci			   const struct fib_entry_notifier_info *fen_info)
46498c2ecf20Sopenharmony_ci{
46508c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib4_entry *fib4_entry;
46518c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node;
46528c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib;
46538c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
46548c2ecf20Sopenharmony_ci
46558c2ecf20Sopenharmony_ci	vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
46568c2ecf20Sopenharmony_ci	if (!vr)
46578c2ecf20Sopenharmony_ci		return NULL;
46588c2ecf20Sopenharmony_ci	fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
46598c2ecf20Sopenharmony_ci
46608c2ecf20Sopenharmony_ci	fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
46618c2ecf20Sopenharmony_ci					    sizeof(fen_info->dst),
46628c2ecf20Sopenharmony_ci					    fen_info->dst_len);
46638c2ecf20Sopenharmony_ci	if (!fib_node)
46648c2ecf20Sopenharmony_ci		return NULL;
46658c2ecf20Sopenharmony_ci
46668c2ecf20Sopenharmony_ci	fib4_entry = container_of(fib_node->fib_entry,
46678c2ecf20Sopenharmony_ci				  struct mlxsw_sp_fib4_entry, common);
46688c2ecf20Sopenharmony_ci	if (fib4_entry->tb_id == fen_info->tb_id &&
46698c2ecf20Sopenharmony_ci	    fib4_entry->tos == fen_info->tos &&
46708c2ecf20Sopenharmony_ci	    fib4_entry->type == fen_info->type &&
46718c2ecf20Sopenharmony_ci	    mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
46728c2ecf20Sopenharmony_ci	    fen_info->fi)
46738c2ecf20Sopenharmony_ci		return fib4_entry;
46748c2ecf20Sopenharmony_ci
46758c2ecf20Sopenharmony_ci	return NULL;
46768c2ecf20Sopenharmony_ci}
46778c2ecf20Sopenharmony_ci
46788c2ecf20Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp_fib_ht_params = {
46798c2ecf20Sopenharmony_ci	.key_offset = offsetof(struct mlxsw_sp_fib_node, key),
46808c2ecf20Sopenharmony_ci	.head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
46818c2ecf20Sopenharmony_ci	.key_len = sizeof(struct mlxsw_sp_fib_key),
46828c2ecf20Sopenharmony_ci	.automatic_shrinking = true,
46838c2ecf20Sopenharmony_ci};
46848c2ecf20Sopenharmony_ci
46858c2ecf20Sopenharmony_cistatic int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
46868c2ecf20Sopenharmony_ci				    struct mlxsw_sp_fib_node *fib_node)
46878c2ecf20Sopenharmony_ci{
46888c2ecf20Sopenharmony_ci	return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
46898c2ecf20Sopenharmony_ci				      mlxsw_sp_fib_ht_params);
46908c2ecf20Sopenharmony_ci}
46918c2ecf20Sopenharmony_ci
46928c2ecf20Sopenharmony_cistatic void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
46938c2ecf20Sopenharmony_ci				     struct mlxsw_sp_fib_node *fib_node)
46948c2ecf20Sopenharmony_ci{
46958c2ecf20Sopenharmony_ci	rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
46968c2ecf20Sopenharmony_ci			       mlxsw_sp_fib_ht_params);
46978c2ecf20Sopenharmony_ci}
46988c2ecf20Sopenharmony_ci
46998c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fib_node *
47008c2ecf20Sopenharmony_cimlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
47018c2ecf20Sopenharmony_ci			 size_t addr_len, unsigned char prefix_len)
47028c2ecf20Sopenharmony_ci{
47038c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_key key;
47048c2ecf20Sopenharmony_ci
47058c2ecf20Sopenharmony_ci	memset(&key, 0, sizeof(key));
47068c2ecf20Sopenharmony_ci	memcpy(key.addr, addr, addr_len);
47078c2ecf20Sopenharmony_ci	key.prefix_len = prefix_len;
47088c2ecf20Sopenharmony_ci	return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
47098c2ecf20Sopenharmony_ci}
47108c2ecf20Sopenharmony_ci
47118c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fib_node *
47128c2ecf20Sopenharmony_cimlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
47138c2ecf20Sopenharmony_ci			 size_t addr_len, unsigned char prefix_len)
47148c2ecf20Sopenharmony_ci{
47158c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node;
47168c2ecf20Sopenharmony_ci
47178c2ecf20Sopenharmony_ci	fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
47188c2ecf20Sopenharmony_ci	if (!fib_node)
47198c2ecf20Sopenharmony_ci		return NULL;
47208c2ecf20Sopenharmony_ci
47218c2ecf20Sopenharmony_ci	list_add(&fib_node->list, &fib->node_list);
47228c2ecf20Sopenharmony_ci	memcpy(fib_node->key.addr, addr, addr_len);
47238c2ecf20Sopenharmony_ci	fib_node->key.prefix_len = prefix_len;
47248c2ecf20Sopenharmony_ci
47258c2ecf20Sopenharmony_ci	return fib_node;
47268c2ecf20Sopenharmony_ci}
47278c2ecf20Sopenharmony_ci
47288c2ecf20Sopenharmony_cistatic void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
47298c2ecf20Sopenharmony_ci{
47308c2ecf20Sopenharmony_ci	list_del(&fib_node->list);
47318c2ecf20Sopenharmony_ci	kfree(fib_node);
47328c2ecf20Sopenharmony_ci}
47338c2ecf20Sopenharmony_ci
47348c2ecf20Sopenharmony_cistatic int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
47358c2ecf20Sopenharmony_ci				      struct mlxsw_sp_fib_node *fib_node)
47368c2ecf20Sopenharmony_ci{
47378c2ecf20Sopenharmony_ci	struct mlxsw_sp_prefix_usage req_prefix_usage;
47388c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib = fib_node->fib;
47398c2ecf20Sopenharmony_ci	struct mlxsw_sp_lpm_tree *lpm_tree;
47408c2ecf20Sopenharmony_ci	int err;
47418c2ecf20Sopenharmony_ci
47428c2ecf20Sopenharmony_ci	lpm_tree = mlxsw_sp->router->lpm.proto_trees[fib->proto];
47438c2ecf20Sopenharmony_ci	if (lpm_tree->prefix_ref_count[fib_node->key.prefix_len] != 0)
47448c2ecf20Sopenharmony_ci		goto out;
47458c2ecf20Sopenharmony_ci
47468c2ecf20Sopenharmony_ci	mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &lpm_tree->prefix_usage);
47478c2ecf20Sopenharmony_ci	mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
47488c2ecf20Sopenharmony_ci	lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
47498c2ecf20Sopenharmony_ci					 fib->proto);
47508c2ecf20Sopenharmony_ci	if (IS_ERR(lpm_tree))
47518c2ecf20Sopenharmony_ci		return PTR_ERR(lpm_tree);
47528c2ecf20Sopenharmony_ci
47538c2ecf20Sopenharmony_ci	err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
47548c2ecf20Sopenharmony_ci	if (err)
47558c2ecf20Sopenharmony_ci		goto err_lpm_tree_replace;
47568c2ecf20Sopenharmony_ci
47578c2ecf20Sopenharmony_ciout:
47588c2ecf20Sopenharmony_ci	lpm_tree->prefix_ref_count[fib_node->key.prefix_len]++;
47598c2ecf20Sopenharmony_ci	return 0;
47608c2ecf20Sopenharmony_ci
47618c2ecf20Sopenharmony_cierr_lpm_tree_replace:
47628c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
47638c2ecf20Sopenharmony_ci	return err;
47648c2ecf20Sopenharmony_ci}
47658c2ecf20Sopenharmony_ci
47668c2ecf20Sopenharmony_cistatic void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
47678c2ecf20Sopenharmony_ci					 struct mlxsw_sp_fib_node *fib_node)
47688c2ecf20Sopenharmony_ci{
47698c2ecf20Sopenharmony_ci	struct mlxsw_sp_lpm_tree *lpm_tree = fib_node->fib->lpm_tree;
47708c2ecf20Sopenharmony_ci	struct mlxsw_sp_prefix_usage req_prefix_usage;
47718c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib = fib_node->fib;
47728c2ecf20Sopenharmony_ci	int err;
47738c2ecf20Sopenharmony_ci
47748c2ecf20Sopenharmony_ci	if (--lpm_tree->prefix_ref_count[fib_node->key.prefix_len] != 0)
47758c2ecf20Sopenharmony_ci		return;
47768c2ecf20Sopenharmony_ci	/* Try to construct a new LPM tree from the current prefix usage
47778c2ecf20Sopenharmony_ci	 * minus the unused one. If we fail, continue using the old one.
47788c2ecf20Sopenharmony_ci	 */
47798c2ecf20Sopenharmony_ci	mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &lpm_tree->prefix_usage);
47808c2ecf20Sopenharmony_ci	mlxsw_sp_prefix_usage_clear(&req_prefix_usage,
47818c2ecf20Sopenharmony_ci				    fib_node->key.prefix_len);
47828c2ecf20Sopenharmony_ci	lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
47838c2ecf20Sopenharmony_ci					 fib->proto);
47848c2ecf20Sopenharmony_ci	if (IS_ERR(lpm_tree))
47858c2ecf20Sopenharmony_ci		return;
47868c2ecf20Sopenharmony_ci
47878c2ecf20Sopenharmony_ci	err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
47888c2ecf20Sopenharmony_ci	if (err)
47898c2ecf20Sopenharmony_ci		goto err_lpm_tree_replace;
47908c2ecf20Sopenharmony_ci
47918c2ecf20Sopenharmony_ci	return;
47928c2ecf20Sopenharmony_ci
47938c2ecf20Sopenharmony_cierr_lpm_tree_replace:
47948c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
47958c2ecf20Sopenharmony_ci}
47968c2ecf20Sopenharmony_ci
47978c2ecf20Sopenharmony_cistatic int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
47988c2ecf20Sopenharmony_ci				  struct mlxsw_sp_fib_node *fib_node,
47998c2ecf20Sopenharmony_ci				  struct mlxsw_sp_fib *fib)
48008c2ecf20Sopenharmony_ci{
48018c2ecf20Sopenharmony_ci	int err;
48028c2ecf20Sopenharmony_ci
48038c2ecf20Sopenharmony_ci	err = mlxsw_sp_fib_node_insert(fib, fib_node);
48048c2ecf20Sopenharmony_ci	if (err)
48058c2ecf20Sopenharmony_ci		return err;
48068c2ecf20Sopenharmony_ci	fib_node->fib = fib;
48078c2ecf20Sopenharmony_ci
48088c2ecf20Sopenharmony_ci	err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib_node);
48098c2ecf20Sopenharmony_ci	if (err)
48108c2ecf20Sopenharmony_ci		goto err_fib_lpm_tree_link;
48118c2ecf20Sopenharmony_ci
48128c2ecf20Sopenharmony_ci	return 0;
48138c2ecf20Sopenharmony_ci
48148c2ecf20Sopenharmony_cierr_fib_lpm_tree_link:
48158c2ecf20Sopenharmony_ci	fib_node->fib = NULL;
48168c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_remove(fib, fib_node);
48178c2ecf20Sopenharmony_ci	return err;
48188c2ecf20Sopenharmony_ci}
48198c2ecf20Sopenharmony_ci
48208c2ecf20Sopenharmony_cistatic void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
48218c2ecf20Sopenharmony_ci				   struct mlxsw_sp_fib_node *fib_node)
48228c2ecf20Sopenharmony_ci{
48238c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib = fib_node->fib;
48248c2ecf20Sopenharmony_ci
48258c2ecf20Sopenharmony_ci	mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib_node);
48268c2ecf20Sopenharmony_ci	fib_node->fib = NULL;
48278c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_remove(fib, fib_node);
48288c2ecf20Sopenharmony_ci}
48298c2ecf20Sopenharmony_ci
48308c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fib_node *
48318c2ecf20Sopenharmony_cimlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
48328c2ecf20Sopenharmony_ci		      size_t addr_len, unsigned char prefix_len,
48338c2ecf20Sopenharmony_ci		      enum mlxsw_sp_l3proto proto)
48348c2ecf20Sopenharmony_ci{
48358c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node;
48368c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib;
48378c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
48388c2ecf20Sopenharmony_ci	int err;
48398c2ecf20Sopenharmony_ci
48408c2ecf20Sopenharmony_ci	vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id, NULL);
48418c2ecf20Sopenharmony_ci	if (IS_ERR(vr))
48428c2ecf20Sopenharmony_ci		return ERR_CAST(vr);
48438c2ecf20Sopenharmony_ci	fib = mlxsw_sp_vr_fib(vr, proto);
48448c2ecf20Sopenharmony_ci
48458c2ecf20Sopenharmony_ci	fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
48468c2ecf20Sopenharmony_ci	if (fib_node)
48478c2ecf20Sopenharmony_ci		return fib_node;
48488c2ecf20Sopenharmony_ci
48498c2ecf20Sopenharmony_ci	fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
48508c2ecf20Sopenharmony_ci	if (!fib_node) {
48518c2ecf20Sopenharmony_ci		err = -ENOMEM;
48528c2ecf20Sopenharmony_ci		goto err_fib_node_create;
48538c2ecf20Sopenharmony_ci	}
48548c2ecf20Sopenharmony_ci
48558c2ecf20Sopenharmony_ci	err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
48568c2ecf20Sopenharmony_ci	if (err)
48578c2ecf20Sopenharmony_ci		goto err_fib_node_init;
48588c2ecf20Sopenharmony_ci
48598c2ecf20Sopenharmony_ci	return fib_node;
48608c2ecf20Sopenharmony_ci
48618c2ecf20Sopenharmony_cierr_fib_node_init:
48628c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_destroy(fib_node);
48638c2ecf20Sopenharmony_cierr_fib_node_create:
48648c2ecf20Sopenharmony_ci	mlxsw_sp_vr_put(mlxsw_sp, vr);
48658c2ecf20Sopenharmony_ci	return ERR_PTR(err);
48668c2ecf20Sopenharmony_ci}
48678c2ecf20Sopenharmony_ci
48688c2ecf20Sopenharmony_cistatic void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
48698c2ecf20Sopenharmony_ci				  struct mlxsw_sp_fib_node *fib_node)
48708c2ecf20Sopenharmony_ci{
48718c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr = fib_node->fib->vr;
48728c2ecf20Sopenharmony_ci
48738c2ecf20Sopenharmony_ci	if (fib_node->fib_entry)
48748c2ecf20Sopenharmony_ci		return;
48758c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
48768c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_destroy(fib_node);
48778c2ecf20Sopenharmony_ci	mlxsw_sp_vr_put(mlxsw_sp, vr);
48788c2ecf20Sopenharmony_ci}
48798c2ecf20Sopenharmony_ci
48808c2ecf20Sopenharmony_cistatic int mlxsw_sp_fib_node_entry_link(struct mlxsw_sp *mlxsw_sp,
48818c2ecf20Sopenharmony_ci					struct mlxsw_sp_fib_entry *fib_entry)
48828c2ecf20Sopenharmony_ci{
48838c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
48848c2ecf20Sopenharmony_ci	int err;
48858c2ecf20Sopenharmony_ci
48868c2ecf20Sopenharmony_ci	fib_node->fib_entry = fib_entry;
48878c2ecf20Sopenharmony_ci
48888c2ecf20Sopenharmony_ci	err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
48898c2ecf20Sopenharmony_ci	if (err)
48908c2ecf20Sopenharmony_ci		goto err_fib_entry_update;
48918c2ecf20Sopenharmony_ci
48928c2ecf20Sopenharmony_ci	return 0;
48938c2ecf20Sopenharmony_ci
48948c2ecf20Sopenharmony_cierr_fib_entry_update:
48958c2ecf20Sopenharmony_ci	fib_node->fib_entry = NULL;
48968c2ecf20Sopenharmony_ci	return err;
48978c2ecf20Sopenharmony_ci}
48988c2ecf20Sopenharmony_ci
48998c2ecf20Sopenharmony_cistatic void
49008c2ecf20Sopenharmony_cimlxsw_sp_fib_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
49018c2ecf20Sopenharmony_ci			       struct mlxsw_sp_fib_entry *fib_entry)
49028c2ecf20Sopenharmony_ci{
49038c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
49048c2ecf20Sopenharmony_ci
49058c2ecf20Sopenharmony_ci	mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
49068c2ecf20Sopenharmony_ci	fib_node->fib_entry = NULL;
49078c2ecf20Sopenharmony_ci}
49088c2ecf20Sopenharmony_ci
49098c2ecf20Sopenharmony_cistatic bool mlxsw_sp_fib4_allow_replace(struct mlxsw_sp_fib4_entry *fib4_entry)
49108c2ecf20Sopenharmony_ci{
49118c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
49128c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib4_entry *fib4_replaced;
49138c2ecf20Sopenharmony_ci
49148c2ecf20Sopenharmony_ci	if (!fib_node->fib_entry)
49158c2ecf20Sopenharmony_ci		return true;
49168c2ecf20Sopenharmony_ci
49178c2ecf20Sopenharmony_ci	fib4_replaced = container_of(fib_node->fib_entry,
49188c2ecf20Sopenharmony_ci				     struct mlxsw_sp_fib4_entry, common);
49198c2ecf20Sopenharmony_ci	if (fib4_entry->tb_id == RT_TABLE_MAIN &&
49208c2ecf20Sopenharmony_ci	    fib4_replaced->tb_id == RT_TABLE_LOCAL)
49218c2ecf20Sopenharmony_ci		return false;
49228c2ecf20Sopenharmony_ci
49238c2ecf20Sopenharmony_ci	return true;
49248c2ecf20Sopenharmony_ci}
49258c2ecf20Sopenharmony_ci
49268c2ecf20Sopenharmony_cistatic int
49278c2ecf20Sopenharmony_cimlxsw_sp_router_fib4_replace(struct mlxsw_sp *mlxsw_sp,
49288c2ecf20Sopenharmony_ci			     const struct fib_entry_notifier_info *fen_info)
49298c2ecf20Sopenharmony_ci{
49308c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib4_entry *fib4_entry, *fib4_replaced;
49318c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_entry *replaced;
49328c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node;
49338c2ecf20Sopenharmony_ci	int err;
49348c2ecf20Sopenharmony_ci
49358c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->aborted)
49368c2ecf20Sopenharmony_ci		return 0;
49378c2ecf20Sopenharmony_ci
49388c2ecf20Sopenharmony_ci	fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
49398c2ecf20Sopenharmony_ci					 &fen_info->dst, sizeof(fen_info->dst),
49408c2ecf20Sopenharmony_ci					 fen_info->dst_len,
49418c2ecf20Sopenharmony_ci					 MLXSW_SP_L3_PROTO_IPV4);
49428c2ecf20Sopenharmony_ci	if (IS_ERR(fib_node)) {
49438c2ecf20Sopenharmony_ci		dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
49448c2ecf20Sopenharmony_ci		return PTR_ERR(fib_node);
49458c2ecf20Sopenharmony_ci	}
49468c2ecf20Sopenharmony_ci
49478c2ecf20Sopenharmony_ci	fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
49488c2ecf20Sopenharmony_ci	if (IS_ERR(fib4_entry)) {
49498c2ecf20Sopenharmony_ci		dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
49508c2ecf20Sopenharmony_ci		err = PTR_ERR(fib4_entry);
49518c2ecf20Sopenharmony_ci		goto err_fib4_entry_create;
49528c2ecf20Sopenharmony_ci	}
49538c2ecf20Sopenharmony_ci
49548c2ecf20Sopenharmony_ci	if (!mlxsw_sp_fib4_allow_replace(fib4_entry)) {
49558c2ecf20Sopenharmony_ci		mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
49568c2ecf20Sopenharmony_ci		mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
49578c2ecf20Sopenharmony_ci		return 0;
49588c2ecf20Sopenharmony_ci	}
49598c2ecf20Sopenharmony_ci
49608c2ecf20Sopenharmony_ci	replaced = fib_node->fib_entry;
49618c2ecf20Sopenharmony_ci	err = mlxsw_sp_fib_node_entry_link(mlxsw_sp, &fib4_entry->common);
49628c2ecf20Sopenharmony_ci	if (err) {
49638c2ecf20Sopenharmony_ci		dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
49648c2ecf20Sopenharmony_ci		goto err_fib_node_entry_link;
49658c2ecf20Sopenharmony_ci	}
49668c2ecf20Sopenharmony_ci
49678c2ecf20Sopenharmony_ci	/* Nothing to replace */
49688c2ecf20Sopenharmony_ci	if (!replaced)
49698c2ecf20Sopenharmony_ci		return 0;
49708c2ecf20Sopenharmony_ci
49718c2ecf20Sopenharmony_ci	mlxsw_sp_fib_entry_hw_flags_clear(mlxsw_sp, replaced);
49728c2ecf20Sopenharmony_ci	fib4_replaced = container_of(replaced, struct mlxsw_sp_fib4_entry,
49738c2ecf20Sopenharmony_ci				     common);
49748c2ecf20Sopenharmony_ci	mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_replaced);
49758c2ecf20Sopenharmony_ci
49768c2ecf20Sopenharmony_ci	return 0;
49778c2ecf20Sopenharmony_ci
49788c2ecf20Sopenharmony_cierr_fib_node_entry_link:
49798c2ecf20Sopenharmony_ci	fib_node->fib_entry = replaced;
49808c2ecf20Sopenharmony_ci	mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
49818c2ecf20Sopenharmony_cierr_fib4_entry_create:
49828c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
49838c2ecf20Sopenharmony_ci	return err;
49848c2ecf20Sopenharmony_ci}
49858c2ecf20Sopenharmony_ci
49868c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
49878c2ecf20Sopenharmony_ci				     struct fib_entry_notifier_info *fen_info)
49888c2ecf20Sopenharmony_ci{
49898c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib4_entry *fib4_entry;
49908c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node;
49918c2ecf20Sopenharmony_ci
49928c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->aborted)
49938c2ecf20Sopenharmony_ci		return;
49948c2ecf20Sopenharmony_ci
49958c2ecf20Sopenharmony_ci	fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
49968c2ecf20Sopenharmony_ci	if (!fib4_entry)
49978c2ecf20Sopenharmony_ci		return;
49988c2ecf20Sopenharmony_ci	fib_node = fib4_entry->common.fib_node;
49998c2ecf20Sopenharmony_ci
50008c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &fib4_entry->common);
50018c2ecf20Sopenharmony_ci	mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
50028c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
50038c2ecf20Sopenharmony_ci}
50048c2ecf20Sopenharmony_ci
50058c2ecf20Sopenharmony_cistatic bool mlxsw_sp_fib6_rt_should_ignore(const struct fib6_info *rt)
50068c2ecf20Sopenharmony_ci{
50078c2ecf20Sopenharmony_ci	/* Multicast routes aren't supported, so ignore them. Neighbour
50088c2ecf20Sopenharmony_ci	 * Discovery packets are specifically trapped.
50098c2ecf20Sopenharmony_ci	 */
50108c2ecf20Sopenharmony_ci	if (ipv6_addr_type(&rt->fib6_dst.addr) & IPV6_ADDR_MULTICAST)
50118c2ecf20Sopenharmony_ci		return true;
50128c2ecf20Sopenharmony_ci
50138c2ecf20Sopenharmony_ci	/* Cloned routes are irrelevant in the forwarding path. */
50148c2ecf20Sopenharmony_ci	if (rt->fib6_flags & RTF_CACHE)
50158c2ecf20Sopenharmony_ci		return true;
50168c2ecf20Sopenharmony_ci
50178c2ecf20Sopenharmony_ci	return false;
50188c2ecf20Sopenharmony_ci}
50198c2ecf20Sopenharmony_ci
50208c2ecf20Sopenharmony_cistatic struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct fib6_info *rt)
50218c2ecf20Sopenharmony_ci{
50228c2ecf20Sopenharmony_ci	struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
50238c2ecf20Sopenharmony_ci
50248c2ecf20Sopenharmony_ci	mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
50258c2ecf20Sopenharmony_ci	if (!mlxsw_sp_rt6)
50268c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
50278c2ecf20Sopenharmony_ci
50288c2ecf20Sopenharmony_ci	/* In case of route replace, replaced route is deleted with
50298c2ecf20Sopenharmony_ci	 * no notification. Take reference to prevent accessing freed
50308c2ecf20Sopenharmony_ci	 * memory.
50318c2ecf20Sopenharmony_ci	 */
50328c2ecf20Sopenharmony_ci	mlxsw_sp_rt6->rt = rt;
50338c2ecf20Sopenharmony_ci	fib6_info_hold(rt);
50348c2ecf20Sopenharmony_ci
50358c2ecf20Sopenharmony_ci	return mlxsw_sp_rt6;
50368c2ecf20Sopenharmony_ci}
50378c2ecf20Sopenharmony_ci
50388c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
50398c2ecf20Sopenharmony_cistatic void mlxsw_sp_rt6_release(struct fib6_info *rt)
50408c2ecf20Sopenharmony_ci{
50418c2ecf20Sopenharmony_ci	fib6_info_release(rt);
50428c2ecf20Sopenharmony_ci}
50438c2ecf20Sopenharmony_ci#else
50448c2ecf20Sopenharmony_cistatic void mlxsw_sp_rt6_release(struct fib6_info *rt)
50458c2ecf20Sopenharmony_ci{
50468c2ecf20Sopenharmony_ci}
50478c2ecf20Sopenharmony_ci#endif
50488c2ecf20Sopenharmony_ci
50498c2ecf20Sopenharmony_cistatic void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
50508c2ecf20Sopenharmony_ci{
50518c2ecf20Sopenharmony_ci	struct fib6_nh *fib6_nh = mlxsw_sp_rt6->rt->fib6_nh;
50528c2ecf20Sopenharmony_ci
50538c2ecf20Sopenharmony_ci	fib6_nh->fib_nh_flags &= ~RTNH_F_OFFLOAD;
50548c2ecf20Sopenharmony_ci	mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
50558c2ecf20Sopenharmony_ci	kfree(mlxsw_sp_rt6);
50568c2ecf20Sopenharmony_ci}
50578c2ecf20Sopenharmony_ci
50588c2ecf20Sopenharmony_cistatic struct fib6_info *
50598c2ecf20Sopenharmony_cimlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
50608c2ecf20Sopenharmony_ci{
50618c2ecf20Sopenharmony_ci	return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
50628c2ecf20Sopenharmony_ci				list)->rt;
50638c2ecf20Sopenharmony_ci}
50648c2ecf20Sopenharmony_ci
50658c2ecf20Sopenharmony_cistatic struct mlxsw_sp_rt6 *
50668c2ecf20Sopenharmony_cimlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
50678c2ecf20Sopenharmony_ci			    const struct fib6_info *rt)
50688c2ecf20Sopenharmony_ci{
50698c2ecf20Sopenharmony_ci	struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
50708c2ecf20Sopenharmony_ci
50718c2ecf20Sopenharmony_ci	list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
50728c2ecf20Sopenharmony_ci		if (mlxsw_sp_rt6->rt == rt)
50738c2ecf20Sopenharmony_ci			return mlxsw_sp_rt6;
50748c2ecf20Sopenharmony_ci	}
50758c2ecf20Sopenharmony_ci
50768c2ecf20Sopenharmony_ci	return NULL;
50778c2ecf20Sopenharmony_ci}
50788c2ecf20Sopenharmony_ci
50798c2ecf20Sopenharmony_cistatic bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp,
50808c2ecf20Sopenharmony_ci					const struct fib6_info *rt,
50818c2ecf20Sopenharmony_ci					enum mlxsw_sp_ipip_type *ret)
50828c2ecf20Sopenharmony_ci{
50838c2ecf20Sopenharmony_ci	return rt->fib6_nh->fib_nh_dev &&
50848c2ecf20Sopenharmony_ci	       mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->fib6_nh->fib_nh_dev, ret);
50858c2ecf20Sopenharmony_ci}
50868c2ecf20Sopenharmony_ci
50878c2ecf20Sopenharmony_cistatic int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp,
50888c2ecf20Sopenharmony_ci				       struct mlxsw_sp_nexthop_group *nh_grp,
50898c2ecf20Sopenharmony_ci				       struct mlxsw_sp_nexthop *nh,
50908c2ecf20Sopenharmony_ci				       const struct fib6_info *rt)
50918c2ecf20Sopenharmony_ci{
50928c2ecf20Sopenharmony_ci	const struct mlxsw_sp_ipip_ops *ipip_ops;
50938c2ecf20Sopenharmony_ci	struct mlxsw_sp_ipip_entry *ipip_entry;
50948c2ecf20Sopenharmony_ci	struct net_device *dev = rt->fib6_nh->fib_nh_dev;
50958c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
50968c2ecf20Sopenharmony_ci	int err;
50978c2ecf20Sopenharmony_ci
50988c2ecf20Sopenharmony_ci	ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, dev);
50998c2ecf20Sopenharmony_ci	if (ipip_entry) {
51008c2ecf20Sopenharmony_ci		ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
51018c2ecf20Sopenharmony_ci		if (ipip_ops->can_offload(mlxsw_sp, dev,
51028c2ecf20Sopenharmony_ci					  MLXSW_SP_L3_PROTO_IPV6)) {
51038c2ecf20Sopenharmony_ci			nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
51048c2ecf20Sopenharmony_ci			mlxsw_sp_nexthop_ipip_init(mlxsw_sp, nh, ipip_entry);
51058c2ecf20Sopenharmony_ci			return 0;
51068c2ecf20Sopenharmony_ci		}
51078c2ecf20Sopenharmony_ci	}
51088c2ecf20Sopenharmony_ci
51098c2ecf20Sopenharmony_ci	nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
51108c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
51118c2ecf20Sopenharmony_ci	if (!rif)
51128c2ecf20Sopenharmony_ci		return 0;
51138c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_rif_init(nh, rif);
51148c2ecf20Sopenharmony_ci
51158c2ecf20Sopenharmony_ci	err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
51168c2ecf20Sopenharmony_ci	if (err)
51178c2ecf20Sopenharmony_ci		goto err_nexthop_neigh_init;
51188c2ecf20Sopenharmony_ci
51198c2ecf20Sopenharmony_ci	return 0;
51208c2ecf20Sopenharmony_ci
51218c2ecf20Sopenharmony_cierr_nexthop_neigh_init:
51228c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_rif_fini(nh);
51238c2ecf20Sopenharmony_ci	return err;
51248c2ecf20Sopenharmony_ci}
51258c2ecf20Sopenharmony_ci
51268c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop6_type_fini(struct mlxsw_sp *mlxsw_sp,
51278c2ecf20Sopenharmony_ci					struct mlxsw_sp_nexthop *nh)
51288c2ecf20Sopenharmony_ci{
51298c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
51308c2ecf20Sopenharmony_ci}
51318c2ecf20Sopenharmony_ci
51328c2ecf20Sopenharmony_cistatic int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
51338c2ecf20Sopenharmony_ci				  struct mlxsw_sp_nexthop_group *nh_grp,
51348c2ecf20Sopenharmony_ci				  struct mlxsw_sp_nexthop *nh,
51358c2ecf20Sopenharmony_ci				  const struct fib6_info *rt)
51368c2ecf20Sopenharmony_ci{
51378c2ecf20Sopenharmony_ci	struct net_device *dev = rt->fib6_nh->fib_nh_dev;
51388c2ecf20Sopenharmony_ci
51398c2ecf20Sopenharmony_ci	nh->nh_grp = nh_grp;
51408c2ecf20Sopenharmony_ci	nh->nh_weight = rt->fib6_nh->fib_nh_weight;
51418c2ecf20Sopenharmony_ci	memcpy(&nh->gw_addr, &rt->fib6_nh->fib_nh_gw6, sizeof(nh->gw_addr));
51428c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh);
51438c2ecf20Sopenharmony_ci
51448c2ecf20Sopenharmony_ci	list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list);
51458c2ecf20Sopenharmony_ci
51468c2ecf20Sopenharmony_ci	if (!dev)
51478c2ecf20Sopenharmony_ci		return 0;
51488c2ecf20Sopenharmony_ci	nh->ifindex = dev->ifindex;
51498c2ecf20Sopenharmony_ci
51508c2ecf20Sopenharmony_ci	return mlxsw_sp_nexthop6_type_init(mlxsw_sp, nh_grp, nh, rt);
51518c2ecf20Sopenharmony_ci}
51528c2ecf20Sopenharmony_ci
51538c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
51548c2ecf20Sopenharmony_ci				   struct mlxsw_sp_nexthop *nh)
51558c2ecf20Sopenharmony_ci{
51568c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop6_type_fini(mlxsw_sp, nh);
51578c2ecf20Sopenharmony_ci	list_del(&nh->router_list_node);
51588c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh);
51598c2ecf20Sopenharmony_ci}
51608c2ecf20Sopenharmony_ci
51618c2ecf20Sopenharmony_cistatic bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp,
51628c2ecf20Sopenharmony_ci				    const struct fib6_info *rt)
51638c2ecf20Sopenharmony_ci{
51648c2ecf20Sopenharmony_ci	return rt->fib6_nh->fib_nh_gw_family ||
51658c2ecf20Sopenharmony_ci	       mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, NULL);
51668c2ecf20Sopenharmony_ci}
51678c2ecf20Sopenharmony_ci
51688c2ecf20Sopenharmony_cistatic struct mlxsw_sp_nexthop_group *
51698c2ecf20Sopenharmony_cimlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
51708c2ecf20Sopenharmony_ci			       struct mlxsw_sp_fib6_entry *fib6_entry)
51718c2ecf20Sopenharmony_ci{
51728c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group *nh_grp;
51738c2ecf20Sopenharmony_ci	struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
51748c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop *nh;
51758c2ecf20Sopenharmony_ci	int i = 0;
51768c2ecf20Sopenharmony_ci	int err;
51778c2ecf20Sopenharmony_ci
51788c2ecf20Sopenharmony_ci	nh_grp = kzalloc(struct_size(nh_grp, nexthops, fib6_entry->nrt6),
51798c2ecf20Sopenharmony_ci			 GFP_KERNEL);
51808c2ecf20Sopenharmony_ci	if (!nh_grp)
51818c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
51828c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&nh_grp->fib_list);
51838c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
51848c2ecf20Sopenharmony_ci	nh_grp->neigh_tbl = &nd_tbl;
51858c2ecf20Sopenharmony_ci#endif
51868c2ecf20Sopenharmony_ci	mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
51878c2ecf20Sopenharmony_ci					struct mlxsw_sp_rt6, list);
51888c2ecf20Sopenharmony_ci	nh_grp->gateway = mlxsw_sp_rt6_is_gateway(mlxsw_sp, mlxsw_sp_rt6->rt);
51898c2ecf20Sopenharmony_ci	nh_grp->count = fib6_entry->nrt6;
51908c2ecf20Sopenharmony_ci	for (i = 0; i < nh_grp->count; i++) {
51918c2ecf20Sopenharmony_ci		struct fib6_info *rt = mlxsw_sp_rt6->rt;
51928c2ecf20Sopenharmony_ci
51938c2ecf20Sopenharmony_ci		nh = &nh_grp->nexthops[i];
51948c2ecf20Sopenharmony_ci		err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
51958c2ecf20Sopenharmony_ci		if (err)
51968c2ecf20Sopenharmony_ci			goto err_nexthop6_init;
51978c2ecf20Sopenharmony_ci		mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
51988c2ecf20Sopenharmony_ci	}
51998c2ecf20Sopenharmony_ci
52008c2ecf20Sopenharmony_ci	err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
52018c2ecf20Sopenharmony_ci	if (err)
52028c2ecf20Sopenharmony_ci		goto err_nexthop_group_insert;
52038c2ecf20Sopenharmony_ci
52048c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
52058c2ecf20Sopenharmony_ci	return nh_grp;
52068c2ecf20Sopenharmony_ci
52078c2ecf20Sopenharmony_cierr_nexthop_group_insert:
52088c2ecf20Sopenharmony_cierr_nexthop6_init:
52098c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--) {
52108c2ecf20Sopenharmony_ci		nh = &nh_grp->nexthops[i];
52118c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
52128c2ecf20Sopenharmony_ci	}
52138c2ecf20Sopenharmony_ci	kfree(nh_grp);
52148c2ecf20Sopenharmony_ci	return ERR_PTR(err);
52158c2ecf20Sopenharmony_ci}
52168c2ecf20Sopenharmony_ci
52178c2ecf20Sopenharmony_cistatic void
52188c2ecf20Sopenharmony_cimlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
52198c2ecf20Sopenharmony_ci				struct mlxsw_sp_nexthop_group *nh_grp)
52208c2ecf20Sopenharmony_ci{
52218c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop *nh;
52228c2ecf20Sopenharmony_ci	int i = nh_grp->count;
52238c2ecf20Sopenharmony_ci
52248c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
52258c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--) {
52268c2ecf20Sopenharmony_ci		nh = &nh_grp->nexthops[i];
52278c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
52288c2ecf20Sopenharmony_ci	}
52298c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
52308c2ecf20Sopenharmony_ci	WARN_ON(nh_grp->adj_index_valid);
52318c2ecf20Sopenharmony_ci	kfree(nh_grp);
52328c2ecf20Sopenharmony_ci}
52338c2ecf20Sopenharmony_ci
52348c2ecf20Sopenharmony_cistatic int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
52358c2ecf20Sopenharmony_ci				       struct mlxsw_sp_fib6_entry *fib6_entry)
52368c2ecf20Sopenharmony_ci{
52378c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group *nh_grp;
52388c2ecf20Sopenharmony_ci
52398c2ecf20Sopenharmony_ci	nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry);
52408c2ecf20Sopenharmony_ci	if (!nh_grp) {
52418c2ecf20Sopenharmony_ci		nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
52428c2ecf20Sopenharmony_ci		if (IS_ERR(nh_grp))
52438c2ecf20Sopenharmony_ci			return PTR_ERR(nh_grp);
52448c2ecf20Sopenharmony_ci	}
52458c2ecf20Sopenharmony_ci
52468c2ecf20Sopenharmony_ci	list_add_tail(&fib6_entry->common.nexthop_group_node,
52478c2ecf20Sopenharmony_ci		      &nh_grp->fib_list);
52488c2ecf20Sopenharmony_ci	fib6_entry->common.nh_group = nh_grp;
52498c2ecf20Sopenharmony_ci
52508c2ecf20Sopenharmony_ci	/* The route and the nexthop are described by the same struct, so we
52518c2ecf20Sopenharmony_ci	 * need to the update the nexthop offload indication for the new route.
52528c2ecf20Sopenharmony_ci	 */
52538c2ecf20Sopenharmony_ci	__mlxsw_sp_nexthop6_group_offload_refresh(nh_grp, fib6_entry);
52548c2ecf20Sopenharmony_ci
52558c2ecf20Sopenharmony_ci	return 0;
52568c2ecf20Sopenharmony_ci}
52578c2ecf20Sopenharmony_ci
52588c2ecf20Sopenharmony_cistatic void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
52598c2ecf20Sopenharmony_ci					struct mlxsw_sp_fib_entry *fib_entry)
52608c2ecf20Sopenharmony_ci{
52618c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
52628c2ecf20Sopenharmony_ci
52638c2ecf20Sopenharmony_ci	list_del(&fib_entry->nexthop_group_node);
52648c2ecf20Sopenharmony_ci	if (!list_empty(&nh_grp->fib_list))
52658c2ecf20Sopenharmony_ci		return;
52668c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
52678c2ecf20Sopenharmony_ci}
52688c2ecf20Sopenharmony_ci
52698c2ecf20Sopenharmony_cistatic int
52708c2ecf20Sopenharmony_cimlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
52718c2ecf20Sopenharmony_ci			       struct mlxsw_sp_fib6_entry *fib6_entry)
52728c2ecf20Sopenharmony_ci{
52738c2ecf20Sopenharmony_ci	struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
52748c2ecf20Sopenharmony_ci	int err;
52758c2ecf20Sopenharmony_ci
52768c2ecf20Sopenharmony_ci	fib6_entry->common.nh_group = NULL;
52778c2ecf20Sopenharmony_ci	list_del(&fib6_entry->common.nexthop_group_node);
52788c2ecf20Sopenharmony_ci
52798c2ecf20Sopenharmony_ci	err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
52808c2ecf20Sopenharmony_ci	if (err)
52818c2ecf20Sopenharmony_ci		goto err_nexthop6_group_get;
52828c2ecf20Sopenharmony_ci
52838c2ecf20Sopenharmony_ci	/* In case this entry is offloaded, then the adjacency index
52848c2ecf20Sopenharmony_ci	 * currently associated with it in the device's table is that
52858c2ecf20Sopenharmony_ci	 * of the old group. Start using the new one instead.
52868c2ecf20Sopenharmony_ci	 */
52878c2ecf20Sopenharmony_ci	err = mlxsw_sp_fib_entry_update(mlxsw_sp, &fib6_entry->common);
52888c2ecf20Sopenharmony_ci	if (err)
52898c2ecf20Sopenharmony_ci		goto err_fib_entry_update;
52908c2ecf20Sopenharmony_ci
52918c2ecf20Sopenharmony_ci	if (list_empty(&old_nh_grp->fib_list))
52928c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
52938c2ecf20Sopenharmony_ci
52948c2ecf20Sopenharmony_ci	return 0;
52958c2ecf20Sopenharmony_ci
52968c2ecf20Sopenharmony_cierr_fib_entry_update:
52978c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
52988c2ecf20Sopenharmony_cierr_nexthop6_group_get:
52998c2ecf20Sopenharmony_ci	list_add_tail(&fib6_entry->common.nexthop_group_node,
53008c2ecf20Sopenharmony_ci		      &old_nh_grp->fib_list);
53018c2ecf20Sopenharmony_ci	fib6_entry->common.nh_group = old_nh_grp;
53028c2ecf20Sopenharmony_ci	return err;
53038c2ecf20Sopenharmony_ci}
53048c2ecf20Sopenharmony_ci
53058c2ecf20Sopenharmony_cistatic int
53068c2ecf20Sopenharmony_cimlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
53078c2ecf20Sopenharmony_ci				struct mlxsw_sp_fib6_entry *fib6_entry,
53088c2ecf20Sopenharmony_ci				struct fib6_info **rt_arr, unsigned int nrt6)
53098c2ecf20Sopenharmony_ci{
53108c2ecf20Sopenharmony_ci	struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
53118c2ecf20Sopenharmony_ci	int err, i;
53128c2ecf20Sopenharmony_ci
53138c2ecf20Sopenharmony_ci	for (i = 0; i < nrt6; i++) {
53148c2ecf20Sopenharmony_ci		mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt_arr[i]);
53158c2ecf20Sopenharmony_ci		if (IS_ERR(mlxsw_sp_rt6)) {
53168c2ecf20Sopenharmony_ci			err = PTR_ERR(mlxsw_sp_rt6);
53178c2ecf20Sopenharmony_ci			goto err_rt6_create;
53188c2ecf20Sopenharmony_ci		}
53198c2ecf20Sopenharmony_ci
53208c2ecf20Sopenharmony_ci		list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
53218c2ecf20Sopenharmony_ci		fib6_entry->nrt6++;
53228c2ecf20Sopenharmony_ci	}
53238c2ecf20Sopenharmony_ci
53248c2ecf20Sopenharmony_ci	err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
53258c2ecf20Sopenharmony_ci	if (err)
53268c2ecf20Sopenharmony_ci		goto err_nexthop6_group_update;
53278c2ecf20Sopenharmony_ci
53288c2ecf20Sopenharmony_ci	return 0;
53298c2ecf20Sopenharmony_ci
53308c2ecf20Sopenharmony_cierr_nexthop6_group_update:
53318c2ecf20Sopenharmony_ci	i = nrt6;
53328c2ecf20Sopenharmony_cierr_rt6_create:
53338c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--) {
53348c2ecf20Sopenharmony_ci		fib6_entry->nrt6--;
53358c2ecf20Sopenharmony_ci		mlxsw_sp_rt6 = list_last_entry(&fib6_entry->rt6_list,
53368c2ecf20Sopenharmony_ci					       struct mlxsw_sp_rt6, list);
53378c2ecf20Sopenharmony_ci		list_del(&mlxsw_sp_rt6->list);
53388c2ecf20Sopenharmony_ci		mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
53398c2ecf20Sopenharmony_ci	}
53408c2ecf20Sopenharmony_ci	return err;
53418c2ecf20Sopenharmony_ci}
53428c2ecf20Sopenharmony_ci
53438c2ecf20Sopenharmony_cistatic void
53448c2ecf20Sopenharmony_cimlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
53458c2ecf20Sopenharmony_ci				struct mlxsw_sp_fib6_entry *fib6_entry,
53468c2ecf20Sopenharmony_ci				struct fib6_info **rt_arr, unsigned int nrt6)
53478c2ecf20Sopenharmony_ci{
53488c2ecf20Sopenharmony_ci	struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
53498c2ecf20Sopenharmony_ci	int i;
53508c2ecf20Sopenharmony_ci
53518c2ecf20Sopenharmony_ci	for (i = 0; i < nrt6; i++) {
53528c2ecf20Sopenharmony_ci		mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry,
53538c2ecf20Sopenharmony_ci							   rt_arr[i]);
53548c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(!mlxsw_sp_rt6))
53558c2ecf20Sopenharmony_ci			continue;
53568c2ecf20Sopenharmony_ci
53578c2ecf20Sopenharmony_ci		fib6_entry->nrt6--;
53588c2ecf20Sopenharmony_ci		list_del(&mlxsw_sp_rt6->list);
53598c2ecf20Sopenharmony_ci		mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
53608c2ecf20Sopenharmony_ci	}
53618c2ecf20Sopenharmony_ci
53628c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
53638c2ecf20Sopenharmony_ci}
53648c2ecf20Sopenharmony_ci
53658c2ecf20Sopenharmony_cistatic void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp,
53668c2ecf20Sopenharmony_ci					 struct mlxsw_sp_fib_entry *fib_entry,
53678c2ecf20Sopenharmony_ci					 const struct fib6_info *rt)
53688c2ecf20Sopenharmony_ci{
53698c2ecf20Sopenharmony_ci	/* Packets hitting RTF_REJECT routes need to be discarded by the
53708c2ecf20Sopenharmony_ci	 * stack. We can rely on their destination device not having a
53718c2ecf20Sopenharmony_ci	 * RIF (it's the loopback device) and can thus use action type
53728c2ecf20Sopenharmony_ci	 * local, which will cause them to be trapped with a lower
53738c2ecf20Sopenharmony_ci	 * priority than packets that need to be locally received.
53748c2ecf20Sopenharmony_ci	 */
53758c2ecf20Sopenharmony_ci	if (rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST))
53768c2ecf20Sopenharmony_ci		fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
53778c2ecf20Sopenharmony_ci	else if (rt->fib6_type == RTN_BLACKHOLE)
53788c2ecf20Sopenharmony_ci		fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE;
53798c2ecf20Sopenharmony_ci	else if (rt->fib6_flags & RTF_REJECT)
53808c2ecf20Sopenharmony_ci		fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_UNREACHABLE;
53818c2ecf20Sopenharmony_ci	else if (mlxsw_sp_rt6_is_gateway(mlxsw_sp, rt))
53828c2ecf20Sopenharmony_ci		fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
53838c2ecf20Sopenharmony_ci	else
53848c2ecf20Sopenharmony_ci		fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
53858c2ecf20Sopenharmony_ci}
53868c2ecf20Sopenharmony_ci
53878c2ecf20Sopenharmony_cistatic void
53888c2ecf20Sopenharmony_cimlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
53898c2ecf20Sopenharmony_ci{
53908c2ecf20Sopenharmony_ci	struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
53918c2ecf20Sopenharmony_ci
53928c2ecf20Sopenharmony_ci	list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
53938c2ecf20Sopenharmony_ci				 list) {
53948c2ecf20Sopenharmony_ci		fib6_entry->nrt6--;
53958c2ecf20Sopenharmony_ci		list_del(&mlxsw_sp_rt6->list);
53968c2ecf20Sopenharmony_ci		mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
53978c2ecf20Sopenharmony_ci	}
53988c2ecf20Sopenharmony_ci}
53998c2ecf20Sopenharmony_ci
54008c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fib6_entry *
54018c2ecf20Sopenharmony_cimlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
54028c2ecf20Sopenharmony_ci			   struct mlxsw_sp_fib_node *fib_node,
54038c2ecf20Sopenharmony_ci			   struct fib6_info **rt_arr, unsigned int nrt6)
54048c2ecf20Sopenharmony_ci{
54058c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib6_entry *fib6_entry;
54068c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_entry *fib_entry;
54078c2ecf20Sopenharmony_ci	struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
54088c2ecf20Sopenharmony_ci	int err, i;
54098c2ecf20Sopenharmony_ci
54108c2ecf20Sopenharmony_ci	fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
54118c2ecf20Sopenharmony_ci	if (!fib6_entry)
54128c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
54138c2ecf20Sopenharmony_ci	fib_entry = &fib6_entry->common;
54148c2ecf20Sopenharmony_ci
54158c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fib6_entry->rt6_list);
54168c2ecf20Sopenharmony_ci
54178c2ecf20Sopenharmony_ci	for (i = 0; i < nrt6; i++) {
54188c2ecf20Sopenharmony_ci		mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt_arr[i]);
54198c2ecf20Sopenharmony_ci		if (IS_ERR(mlxsw_sp_rt6)) {
54208c2ecf20Sopenharmony_ci			err = PTR_ERR(mlxsw_sp_rt6);
54218c2ecf20Sopenharmony_ci			goto err_rt6_create;
54228c2ecf20Sopenharmony_ci		}
54238c2ecf20Sopenharmony_ci		list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
54248c2ecf20Sopenharmony_ci		fib6_entry->nrt6++;
54258c2ecf20Sopenharmony_ci	}
54268c2ecf20Sopenharmony_ci
54278c2ecf20Sopenharmony_ci	mlxsw_sp_fib6_entry_type_set(mlxsw_sp, fib_entry, rt_arr[0]);
54288c2ecf20Sopenharmony_ci
54298c2ecf20Sopenharmony_ci	err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
54308c2ecf20Sopenharmony_ci	if (err)
54318c2ecf20Sopenharmony_ci		goto err_nexthop6_group_get;
54328c2ecf20Sopenharmony_ci
54338c2ecf20Sopenharmony_ci	fib_entry->fib_node = fib_node;
54348c2ecf20Sopenharmony_ci
54358c2ecf20Sopenharmony_ci	return fib6_entry;
54368c2ecf20Sopenharmony_ci
54378c2ecf20Sopenharmony_cierr_nexthop6_group_get:
54388c2ecf20Sopenharmony_ci	i = nrt6;
54398c2ecf20Sopenharmony_cierr_rt6_create:
54408c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--) {
54418c2ecf20Sopenharmony_ci		fib6_entry->nrt6--;
54428c2ecf20Sopenharmony_ci		mlxsw_sp_rt6 = list_last_entry(&fib6_entry->rt6_list,
54438c2ecf20Sopenharmony_ci					       struct mlxsw_sp_rt6, list);
54448c2ecf20Sopenharmony_ci		list_del(&mlxsw_sp_rt6->list);
54458c2ecf20Sopenharmony_ci		mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
54468c2ecf20Sopenharmony_ci	}
54478c2ecf20Sopenharmony_ci	kfree(fib6_entry);
54488c2ecf20Sopenharmony_ci	return ERR_PTR(err);
54498c2ecf20Sopenharmony_ci}
54508c2ecf20Sopenharmony_ci
54518c2ecf20Sopenharmony_cistatic void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
54528c2ecf20Sopenharmony_ci					struct mlxsw_sp_fib6_entry *fib6_entry)
54538c2ecf20Sopenharmony_ci{
54548c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
54558c2ecf20Sopenharmony_ci	mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
54568c2ecf20Sopenharmony_ci	WARN_ON(fib6_entry->nrt6);
54578c2ecf20Sopenharmony_ci	kfree(fib6_entry);
54588c2ecf20Sopenharmony_ci}
54598c2ecf20Sopenharmony_ci
54608c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fib6_entry *
54618c2ecf20Sopenharmony_cimlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
54628c2ecf20Sopenharmony_ci			   const struct fib6_info *rt)
54638c2ecf20Sopenharmony_ci{
54648c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib6_entry *fib6_entry;
54658c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node;
54668c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib;
54678c2ecf20Sopenharmony_ci	struct fib6_info *cmp_rt;
54688c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
54698c2ecf20Sopenharmony_ci
54708c2ecf20Sopenharmony_ci	vr = mlxsw_sp_vr_find(mlxsw_sp, rt->fib6_table->tb6_id);
54718c2ecf20Sopenharmony_ci	if (!vr)
54728c2ecf20Sopenharmony_ci		return NULL;
54738c2ecf20Sopenharmony_ci	fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
54748c2ecf20Sopenharmony_ci
54758c2ecf20Sopenharmony_ci	fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->fib6_dst.addr,
54768c2ecf20Sopenharmony_ci					    sizeof(rt->fib6_dst.addr),
54778c2ecf20Sopenharmony_ci					    rt->fib6_dst.plen);
54788c2ecf20Sopenharmony_ci	if (!fib_node)
54798c2ecf20Sopenharmony_ci		return NULL;
54808c2ecf20Sopenharmony_ci
54818c2ecf20Sopenharmony_ci	fib6_entry = container_of(fib_node->fib_entry,
54828c2ecf20Sopenharmony_ci				  struct mlxsw_sp_fib6_entry, common);
54838c2ecf20Sopenharmony_ci	cmp_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
54848c2ecf20Sopenharmony_ci	if (rt->fib6_table->tb6_id == cmp_rt->fib6_table->tb6_id &&
54858c2ecf20Sopenharmony_ci	    rt->fib6_metric == cmp_rt->fib6_metric &&
54868c2ecf20Sopenharmony_ci	    mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
54878c2ecf20Sopenharmony_ci		return fib6_entry;
54888c2ecf20Sopenharmony_ci
54898c2ecf20Sopenharmony_ci	return NULL;
54908c2ecf20Sopenharmony_ci}
54918c2ecf20Sopenharmony_ci
54928c2ecf20Sopenharmony_cistatic bool mlxsw_sp_fib6_allow_replace(struct mlxsw_sp_fib6_entry *fib6_entry)
54938c2ecf20Sopenharmony_ci{
54948c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
54958c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib6_entry *fib6_replaced;
54968c2ecf20Sopenharmony_ci	struct fib6_info *rt, *rt_replaced;
54978c2ecf20Sopenharmony_ci
54988c2ecf20Sopenharmony_ci	if (!fib_node->fib_entry)
54998c2ecf20Sopenharmony_ci		return true;
55008c2ecf20Sopenharmony_ci
55018c2ecf20Sopenharmony_ci	fib6_replaced = container_of(fib_node->fib_entry,
55028c2ecf20Sopenharmony_ci				     struct mlxsw_sp_fib6_entry,
55038c2ecf20Sopenharmony_ci				     common);
55048c2ecf20Sopenharmony_ci	rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
55058c2ecf20Sopenharmony_ci	rt_replaced = mlxsw_sp_fib6_entry_rt(fib6_replaced);
55068c2ecf20Sopenharmony_ci	if (rt->fib6_table->tb6_id == RT_TABLE_MAIN &&
55078c2ecf20Sopenharmony_ci	    rt_replaced->fib6_table->tb6_id == RT_TABLE_LOCAL)
55088c2ecf20Sopenharmony_ci		return false;
55098c2ecf20Sopenharmony_ci
55108c2ecf20Sopenharmony_ci	return true;
55118c2ecf20Sopenharmony_ci}
55128c2ecf20Sopenharmony_ci
55138c2ecf20Sopenharmony_cistatic int mlxsw_sp_router_fib6_replace(struct mlxsw_sp *mlxsw_sp,
55148c2ecf20Sopenharmony_ci					struct fib6_info **rt_arr,
55158c2ecf20Sopenharmony_ci					unsigned int nrt6)
55168c2ecf20Sopenharmony_ci{
55178c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib6_entry *fib6_entry, *fib6_replaced;
55188c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_entry *replaced;
55198c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node;
55208c2ecf20Sopenharmony_ci	struct fib6_info *rt = rt_arr[0];
55218c2ecf20Sopenharmony_ci	int err;
55228c2ecf20Sopenharmony_ci
55238c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->aborted)
55248c2ecf20Sopenharmony_ci		return 0;
55258c2ecf20Sopenharmony_ci
55268c2ecf20Sopenharmony_ci	if (rt->fib6_src.plen)
55278c2ecf20Sopenharmony_ci		return -EINVAL;
55288c2ecf20Sopenharmony_ci
55298c2ecf20Sopenharmony_ci	if (mlxsw_sp_fib6_rt_should_ignore(rt))
55308c2ecf20Sopenharmony_ci		return 0;
55318c2ecf20Sopenharmony_ci
55328c2ecf20Sopenharmony_ci	fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->fib6_table->tb6_id,
55338c2ecf20Sopenharmony_ci					 &rt->fib6_dst.addr,
55348c2ecf20Sopenharmony_ci					 sizeof(rt->fib6_dst.addr),
55358c2ecf20Sopenharmony_ci					 rt->fib6_dst.plen,
55368c2ecf20Sopenharmony_ci					 MLXSW_SP_L3_PROTO_IPV6);
55378c2ecf20Sopenharmony_ci	if (IS_ERR(fib_node))
55388c2ecf20Sopenharmony_ci		return PTR_ERR(fib_node);
55398c2ecf20Sopenharmony_ci
55408c2ecf20Sopenharmony_ci	fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt_arr,
55418c2ecf20Sopenharmony_ci						nrt6);
55428c2ecf20Sopenharmony_ci	if (IS_ERR(fib6_entry)) {
55438c2ecf20Sopenharmony_ci		err = PTR_ERR(fib6_entry);
55448c2ecf20Sopenharmony_ci		goto err_fib6_entry_create;
55458c2ecf20Sopenharmony_ci	}
55468c2ecf20Sopenharmony_ci
55478c2ecf20Sopenharmony_ci	if (!mlxsw_sp_fib6_allow_replace(fib6_entry)) {
55488c2ecf20Sopenharmony_ci		mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
55498c2ecf20Sopenharmony_ci		mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
55508c2ecf20Sopenharmony_ci		return 0;
55518c2ecf20Sopenharmony_ci	}
55528c2ecf20Sopenharmony_ci
55538c2ecf20Sopenharmony_ci	replaced = fib_node->fib_entry;
55548c2ecf20Sopenharmony_ci	err = mlxsw_sp_fib_node_entry_link(mlxsw_sp, &fib6_entry->common);
55558c2ecf20Sopenharmony_ci	if (err)
55568c2ecf20Sopenharmony_ci		goto err_fib_node_entry_link;
55578c2ecf20Sopenharmony_ci
55588c2ecf20Sopenharmony_ci	/* Nothing to replace */
55598c2ecf20Sopenharmony_ci	if (!replaced)
55608c2ecf20Sopenharmony_ci		return 0;
55618c2ecf20Sopenharmony_ci
55628c2ecf20Sopenharmony_ci	mlxsw_sp_fib_entry_hw_flags_clear(mlxsw_sp, replaced);
55638c2ecf20Sopenharmony_ci	fib6_replaced = container_of(replaced, struct mlxsw_sp_fib6_entry,
55648c2ecf20Sopenharmony_ci				     common);
55658c2ecf20Sopenharmony_ci	mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_replaced);
55668c2ecf20Sopenharmony_ci
55678c2ecf20Sopenharmony_ci	return 0;
55688c2ecf20Sopenharmony_ci
55698c2ecf20Sopenharmony_cierr_fib_node_entry_link:
55708c2ecf20Sopenharmony_ci	fib_node->fib_entry = replaced;
55718c2ecf20Sopenharmony_ci	mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
55728c2ecf20Sopenharmony_cierr_fib6_entry_create:
55738c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
55748c2ecf20Sopenharmony_ci	return err;
55758c2ecf20Sopenharmony_ci}
55768c2ecf20Sopenharmony_ci
55778c2ecf20Sopenharmony_cistatic int mlxsw_sp_router_fib6_append(struct mlxsw_sp *mlxsw_sp,
55788c2ecf20Sopenharmony_ci				       struct fib6_info **rt_arr,
55798c2ecf20Sopenharmony_ci				       unsigned int nrt6)
55808c2ecf20Sopenharmony_ci{
55818c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib6_entry *fib6_entry;
55828c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node;
55838c2ecf20Sopenharmony_ci	struct fib6_info *rt = rt_arr[0];
55848c2ecf20Sopenharmony_ci	int err;
55858c2ecf20Sopenharmony_ci
55868c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->aborted)
55878c2ecf20Sopenharmony_ci		return 0;
55888c2ecf20Sopenharmony_ci
55898c2ecf20Sopenharmony_ci	if (rt->fib6_src.plen)
55908c2ecf20Sopenharmony_ci		return -EINVAL;
55918c2ecf20Sopenharmony_ci
55928c2ecf20Sopenharmony_ci	if (mlxsw_sp_fib6_rt_should_ignore(rt))
55938c2ecf20Sopenharmony_ci		return 0;
55948c2ecf20Sopenharmony_ci
55958c2ecf20Sopenharmony_ci	fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->fib6_table->tb6_id,
55968c2ecf20Sopenharmony_ci					 &rt->fib6_dst.addr,
55978c2ecf20Sopenharmony_ci					 sizeof(rt->fib6_dst.addr),
55988c2ecf20Sopenharmony_ci					 rt->fib6_dst.plen,
55998c2ecf20Sopenharmony_ci					 MLXSW_SP_L3_PROTO_IPV6);
56008c2ecf20Sopenharmony_ci	if (IS_ERR(fib_node))
56018c2ecf20Sopenharmony_ci		return PTR_ERR(fib_node);
56028c2ecf20Sopenharmony_ci
56038c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!fib_node->fib_entry)) {
56048c2ecf20Sopenharmony_ci		mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
56058c2ecf20Sopenharmony_ci		return -EINVAL;
56068c2ecf20Sopenharmony_ci	}
56078c2ecf20Sopenharmony_ci
56088c2ecf20Sopenharmony_ci	fib6_entry = container_of(fib_node->fib_entry,
56098c2ecf20Sopenharmony_ci				  struct mlxsw_sp_fib6_entry, common);
56108c2ecf20Sopenharmony_ci	err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt_arr,
56118c2ecf20Sopenharmony_ci					      nrt6);
56128c2ecf20Sopenharmony_ci	if (err)
56138c2ecf20Sopenharmony_ci		goto err_fib6_entry_nexthop_add;
56148c2ecf20Sopenharmony_ci
56158c2ecf20Sopenharmony_ci	return 0;
56168c2ecf20Sopenharmony_ci
56178c2ecf20Sopenharmony_cierr_fib6_entry_nexthop_add:
56188c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
56198c2ecf20Sopenharmony_ci	return err;
56208c2ecf20Sopenharmony_ci}
56218c2ecf20Sopenharmony_ci
56228c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
56238c2ecf20Sopenharmony_ci				     struct fib6_info **rt_arr,
56248c2ecf20Sopenharmony_ci				     unsigned int nrt6)
56258c2ecf20Sopenharmony_ci{
56268c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib6_entry *fib6_entry;
56278c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node;
56288c2ecf20Sopenharmony_ci	struct fib6_info *rt = rt_arr[0];
56298c2ecf20Sopenharmony_ci
56308c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->aborted)
56318c2ecf20Sopenharmony_ci		return;
56328c2ecf20Sopenharmony_ci
56338c2ecf20Sopenharmony_ci	if (mlxsw_sp_fib6_rt_should_ignore(rt))
56348c2ecf20Sopenharmony_ci		return;
56358c2ecf20Sopenharmony_ci
56368c2ecf20Sopenharmony_ci	/* Multipath routes are first added to the FIB trie and only then
56378c2ecf20Sopenharmony_ci	 * notified. If we vetoed the addition, we will get a delete
56388c2ecf20Sopenharmony_ci	 * notification for a route we do not have. Therefore, do not warn if
56398c2ecf20Sopenharmony_ci	 * route was not found.
56408c2ecf20Sopenharmony_ci	 */
56418c2ecf20Sopenharmony_ci	fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
56428c2ecf20Sopenharmony_ci	if (!fib6_entry)
56438c2ecf20Sopenharmony_ci		return;
56448c2ecf20Sopenharmony_ci
56458c2ecf20Sopenharmony_ci	/* If not all the nexthops are deleted, then only reduce the nexthop
56468c2ecf20Sopenharmony_ci	 * group.
56478c2ecf20Sopenharmony_ci	 */
56488c2ecf20Sopenharmony_ci	if (nrt6 != fib6_entry->nrt6) {
56498c2ecf20Sopenharmony_ci		mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt_arr,
56508c2ecf20Sopenharmony_ci						nrt6);
56518c2ecf20Sopenharmony_ci		return;
56528c2ecf20Sopenharmony_ci	}
56538c2ecf20Sopenharmony_ci
56548c2ecf20Sopenharmony_ci	fib_node = fib6_entry->common.fib_node;
56558c2ecf20Sopenharmony_ci
56568c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &fib6_entry->common);
56578c2ecf20Sopenharmony_ci	mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
56588c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
56598c2ecf20Sopenharmony_ci}
56608c2ecf20Sopenharmony_ci
56618c2ecf20Sopenharmony_cistatic int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
56628c2ecf20Sopenharmony_ci					    enum mlxsw_reg_ralxx_protocol proto,
56638c2ecf20Sopenharmony_ci					    u8 tree_id)
56648c2ecf20Sopenharmony_ci{
56658c2ecf20Sopenharmony_ci	char ralta_pl[MLXSW_REG_RALTA_LEN];
56668c2ecf20Sopenharmony_ci	char ralst_pl[MLXSW_REG_RALST_LEN];
56678c2ecf20Sopenharmony_ci	int i, err;
56688c2ecf20Sopenharmony_ci
56698c2ecf20Sopenharmony_ci	mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
56708c2ecf20Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
56718c2ecf20Sopenharmony_ci	if (err)
56728c2ecf20Sopenharmony_ci		return err;
56738c2ecf20Sopenharmony_ci
56748c2ecf20Sopenharmony_ci	mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
56758c2ecf20Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
56768c2ecf20Sopenharmony_ci	if (err)
56778c2ecf20Sopenharmony_ci		return err;
56788c2ecf20Sopenharmony_ci
56798c2ecf20Sopenharmony_ci	for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
56808c2ecf20Sopenharmony_ci		struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
56818c2ecf20Sopenharmony_ci		char raltb_pl[MLXSW_REG_RALTB_LEN];
56828c2ecf20Sopenharmony_ci		char ralue_pl[MLXSW_REG_RALUE_LEN];
56838c2ecf20Sopenharmony_ci
56848c2ecf20Sopenharmony_ci		mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
56858c2ecf20Sopenharmony_ci		err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
56868c2ecf20Sopenharmony_ci				      raltb_pl);
56878c2ecf20Sopenharmony_ci		if (err)
56888c2ecf20Sopenharmony_ci			return err;
56898c2ecf20Sopenharmony_ci
56908c2ecf20Sopenharmony_ci		mlxsw_reg_ralue_pack(ralue_pl, proto,
56918c2ecf20Sopenharmony_ci				     MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
56928c2ecf20Sopenharmony_ci		mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
56938c2ecf20Sopenharmony_ci		err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
56948c2ecf20Sopenharmony_ci				      ralue_pl);
56958c2ecf20Sopenharmony_ci		if (err)
56968c2ecf20Sopenharmony_ci			return err;
56978c2ecf20Sopenharmony_ci	}
56988c2ecf20Sopenharmony_ci
56998c2ecf20Sopenharmony_ci	return 0;
57008c2ecf20Sopenharmony_ci}
57018c2ecf20Sopenharmony_ci
57028c2ecf20Sopenharmony_cistatic struct mlxsw_sp_mr_table *
57038c2ecf20Sopenharmony_cimlxsw_sp_router_fibmr_family_to_table(struct mlxsw_sp_vr *vr, int family)
57048c2ecf20Sopenharmony_ci{
57058c2ecf20Sopenharmony_ci	if (family == RTNL_FAMILY_IPMR)
57068c2ecf20Sopenharmony_ci		return vr->mr_table[MLXSW_SP_L3_PROTO_IPV4];
57078c2ecf20Sopenharmony_ci	else
57088c2ecf20Sopenharmony_ci		return vr->mr_table[MLXSW_SP_L3_PROTO_IPV6];
57098c2ecf20Sopenharmony_ci}
57108c2ecf20Sopenharmony_ci
57118c2ecf20Sopenharmony_cistatic int mlxsw_sp_router_fibmr_add(struct mlxsw_sp *mlxsw_sp,
57128c2ecf20Sopenharmony_ci				     struct mfc_entry_notifier_info *men_info,
57138c2ecf20Sopenharmony_ci				     bool replace)
57148c2ecf20Sopenharmony_ci{
57158c2ecf20Sopenharmony_ci	struct mlxsw_sp_mr_table *mrt;
57168c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
57178c2ecf20Sopenharmony_ci
57188c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->aborted)
57198c2ecf20Sopenharmony_ci		return 0;
57208c2ecf20Sopenharmony_ci
57218c2ecf20Sopenharmony_ci	vr = mlxsw_sp_vr_get(mlxsw_sp, men_info->tb_id, NULL);
57228c2ecf20Sopenharmony_ci	if (IS_ERR(vr))
57238c2ecf20Sopenharmony_ci		return PTR_ERR(vr);
57248c2ecf20Sopenharmony_ci
57258c2ecf20Sopenharmony_ci	mrt = mlxsw_sp_router_fibmr_family_to_table(vr, men_info->info.family);
57268c2ecf20Sopenharmony_ci	return mlxsw_sp_mr_route_add(mrt, men_info->mfc, replace);
57278c2ecf20Sopenharmony_ci}
57288c2ecf20Sopenharmony_ci
57298c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_fibmr_del(struct mlxsw_sp *mlxsw_sp,
57308c2ecf20Sopenharmony_ci				      struct mfc_entry_notifier_info *men_info)
57318c2ecf20Sopenharmony_ci{
57328c2ecf20Sopenharmony_ci	struct mlxsw_sp_mr_table *mrt;
57338c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
57348c2ecf20Sopenharmony_ci
57358c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->aborted)
57368c2ecf20Sopenharmony_ci		return;
57378c2ecf20Sopenharmony_ci
57388c2ecf20Sopenharmony_ci	vr = mlxsw_sp_vr_find(mlxsw_sp, men_info->tb_id);
57398c2ecf20Sopenharmony_ci	if (WARN_ON(!vr))
57408c2ecf20Sopenharmony_ci		return;
57418c2ecf20Sopenharmony_ci
57428c2ecf20Sopenharmony_ci	mrt = mlxsw_sp_router_fibmr_family_to_table(vr, men_info->info.family);
57438c2ecf20Sopenharmony_ci	mlxsw_sp_mr_route_del(mrt, men_info->mfc);
57448c2ecf20Sopenharmony_ci	mlxsw_sp_vr_put(mlxsw_sp, vr);
57458c2ecf20Sopenharmony_ci}
57468c2ecf20Sopenharmony_ci
57478c2ecf20Sopenharmony_cistatic int
57488c2ecf20Sopenharmony_cimlxsw_sp_router_fibmr_vif_add(struct mlxsw_sp *mlxsw_sp,
57498c2ecf20Sopenharmony_ci			      struct vif_entry_notifier_info *ven_info)
57508c2ecf20Sopenharmony_ci{
57518c2ecf20Sopenharmony_ci	struct mlxsw_sp_mr_table *mrt;
57528c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
57538c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
57548c2ecf20Sopenharmony_ci
57558c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->aborted)
57568c2ecf20Sopenharmony_ci		return 0;
57578c2ecf20Sopenharmony_ci
57588c2ecf20Sopenharmony_ci	vr = mlxsw_sp_vr_get(mlxsw_sp, ven_info->tb_id, NULL);
57598c2ecf20Sopenharmony_ci	if (IS_ERR(vr))
57608c2ecf20Sopenharmony_ci		return PTR_ERR(vr);
57618c2ecf20Sopenharmony_ci
57628c2ecf20Sopenharmony_ci	mrt = mlxsw_sp_router_fibmr_family_to_table(vr, ven_info->info.family);
57638c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, ven_info->dev);
57648c2ecf20Sopenharmony_ci	return mlxsw_sp_mr_vif_add(mrt, ven_info->dev,
57658c2ecf20Sopenharmony_ci				   ven_info->vif_index,
57668c2ecf20Sopenharmony_ci				   ven_info->vif_flags, rif);
57678c2ecf20Sopenharmony_ci}
57688c2ecf20Sopenharmony_ci
57698c2ecf20Sopenharmony_cistatic void
57708c2ecf20Sopenharmony_cimlxsw_sp_router_fibmr_vif_del(struct mlxsw_sp *mlxsw_sp,
57718c2ecf20Sopenharmony_ci			      struct vif_entry_notifier_info *ven_info)
57728c2ecf20Sopenharmony_ci{
57738c2ecf20Sopenharmony_ci	struct mlxsw_sp_mr_table *mrt;
57748c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
57758c2ecf20Sopenharmony_ci
57768c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->aborted)
57778c2ecf20Sopenharmony_ci		return;
57788c2ecf20Sopenharmony_ci
57798c2ecf20Sopenharmony_ci	vr = mlxsw_sp_vr_find(mlxsw_sp, ven_info->tb_id);
57808c2ecf20Sopenharmony_ci	if (WARN_ON(!vr))
57818c2ecf20Sopenharmony_ci		return;
57828c2ecf20Sopenharmony_ci
57838c2ecf20Sopenharmony_ci	mrt = mlxsw_sp_router_fibmr_family_to_table(vr, ven_info->info.family);
57848c2ecf20Sopenharmony_ci	mlxsw_sp_mr_vif_del(mrt, ven_info->vif_index);
57858c2ecf20Sopenharmony_ci	mlxsw_sp_vr_put(mlxsw_sp, vr);
57868c2ecf20Sopenharmony_ci}
57878c2ecf20Sopenharmony_ci
57888c2ecf20Sopenharmony_cistatic int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
57898c2ecf20Sopenharmony_ci{
57908c2ecf20Sopenharmony_ci	enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
57918c2ecf20Sopenharmony_ci	int err;
57928c2ecf20Sopenharmony_ci
57938c2ecf20Sopenharmony_ci	err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
57948c2ecf20Sopenharmony_ci					       MLXSW_SP_LPM_TREE_MIN);
57958c2ecf20Sopenharmony_ci	if (err)
57968c2ecf20Sopenharmony_ci		return err;
57978c2ecf20Sopenharmony_ci
57988c2ecf20Sopenharmony_ci	/* The multicast router code does not need an abort trap as by default,
57998c2ecf20Sopenharmony_ci	 * packets that don't match any routes are trapped to the CPU.
58008c2ecf20Sopenharmony_ci	 */
58018c2ecf20Sopenharmony_ci
58028c2ecf20Sopenharmony_ci	proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
58038c2ecf20Sopenharmony_ci	return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
58048c2ecf20Sopenharmony_ci						MLXSW_SP_LPM_TREE_MIN + 1);
58058c2ecf20Sopenharmony_ci}
58068c2ecf20Sopenharmony_ci
58078c2ecf20Sopenharmony_cistatic void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
58088c2ecf20Sopenharmony_ci				     struct mlxsw_sp_fib_node *fib_node)
58098c2ecf20Sopenharmony_ci{
58108c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib4_entry *fib4_entry;
58118c2ecf20Sopenharmony_ci
58128c2ecf20Sopenharmony_ci	fib4_entry = container_of(fib_node->fib_entry,
58138c2ecf20Sopenharmony_ci				  struct mlxsw_sp_fib4_entry, common);
58148c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, fib_node->fib_entry);
58158c2ecf20Sopenharmony_ci	mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
58168c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
58178c2ecf20Sopenharmony_ci}
58188c2ecf20Sopenharmony_ci
58198c2ecf20Sopenharmony_cistatic void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
58208c2ecf20Sopenharmony_ci				     struct mlxsw_sp_fib_node *fib_node)
58218c2ecf20Sopenharmony_ci{
58228c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib6_entry *fib6_entry;
58238c2ecf20Sopenharmony_ci
58248c2ecf20Sopenharmony_ci	fib6_entry = container_of(fib_node->fib_entry,
58258c2ecf20Sopenharmony_ci				  struct mlxsw_sp_fib6_entry, common);
58268c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, fib_node->fib_entry);
58278c2ecf20Sopenharmony_ci	mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
58288c2ecf20Sopenharmony_ci	mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
58298c2ecf20Sopenharmony_ci}
58308c2ecf20Sopenharmony_ci
58318c2ecf20Sopenharmony_cistatic void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
58328c2ecf20Sopenharmony_ci				    struct mlxsw_sp_fib_node *fib_node)
58338c2ecf20Sopenharmony_ci{
58348c2ecf20Sopenharmony_ci	switch (fib_node->fib->proto) {
58358c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
58368c2ecf20Sopenharmony_ci		mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
58378c2ecf20Sopenharmony_ci		break;
58388c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
58398c2ecf20Sopenharmony_ci		mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
58408c2ecf20Sopenharmony_ci		break;
58418c2ecf20Sopenharmony_ci	}
58428c2ecf20Sopenharmony_ci}
58438c2ecf20Sopenharmony_ci
58448c2ecf20Sopenharmony_cistatic void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
58458c2ecf20Sopenharmony_ci				  struct mlxsw_sp_vr *vr,
58468c2ecf20Sopenharmony_ci				  enum mlxsw_sp_l3proto proto)
58478c2ecf20Sopenharmony_ci{
58488c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
58498c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_node *fib_node, *tmp;
58508c2ecf20Sopenharmony_ci
58518c2ecf20Sopenharmony_ci	list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
58528c2ecf20Sopenharmony_ci		bool do_break = &tmp->list == &fib->node_list;
58538c2ecf20Sopenharmony_ci
58548c2ecf20Sopenharmony_ci		mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
58558c2ecf20Sopenharmony_ci		if (do_break)
58568c2ecf20Sopenharmony_ci			break;
58578c2ecf20Sopenharmony_ci	}
58588c2ecf20Sopenharmony_ci}
58598c2ecf20Sopenharmony_ci
58608c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
58618c2ecf20Sopenharmony_ci{
58628c2ecf20Sopenharmony_ci	int i, j;
58638c2ecf20Sopenharmony_ci
58648c2ecf20Sopenharmony_ci	for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
58658c2ecf20Sopenharmony_ci		struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
58668c2ecf20Sopenharmony_ci
58678c2ecf20Sopenharmony_ci		if (!mlxsw_sp_vr_is_used(vr))
58688c2ecf20Sopenharmony_ci			continue;
58698c2ecf20Sopenharmony_ci
58708c2ecf20Sopenharmony_ci		for (j = 0; j < MLXSW_SP_L3_PROTO_MAX; j++)
58718c2ecf20Sopenharmony_ci			mlxsw_sp_mr_table_flush(vr->mr_table[j]);
58728c2ecf20Sopenharmony_ci		mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
58738c2ecf20Sopenharmony_ci
58748c2ecf20Sopenharmony_ci		/* If virtual router was only used for IPv4, then it's no
58758c2ecf20Sopenharmony_ci		 * longer used.
58768c2ecf20Sopenharmony_ci		 */
58778c2ecf20Sopenharmony_ci		if (!mlxsw_sp_vr_is_used(vr))
58788c2ecf20Sopenharmony_ci			continue;
58798c2ecf20Sopenharmony_ci		mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
58808c2ecf20Sopenharmony_ci	}
58818c2ecf20Sopenharmony_ci
58828c2ecf20Sopenharmony_ci	/* After flushing all the routes, it is not possible anyone is still
58838c2ecf20Sopenharmony_ci	 * using the adjacency index that is discarding packets, so free it in
58848c2ecf20Sopenharmony_ci	 * case it was allocated.
58858c2ecf20Sopenharmony_ci	 */
58868c2ecf20Sopenharmony_ci	if (!mlxsw_sp->router->adj_discard_index_valid)
58878c2ecf20Sopenharmony_ci		return;
58888c2ecf20Sopenharmony_ci	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
58898c2ecf20Sopenharmony_ci			   mlxsw_sp->router->adj_discard_index);
58908c2ecf20Sopenharmony_ci	mlxsw_sp->router->adj_discard_index_valid = false;
58918c2ecf20Sopenharmony_ci}
58928c2ecf20Sopenharmony_ci
58938c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
58948c2ecf20Sopenharmony_ci{
58958c2ecf20Sopenharmony_ci	int err;
58968c2ecf20Sopenharmony_ci
58978c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->aborted)
58988c2ecf20Sopenharmony_ci		return;
58998c2ecf20Sopenharmony_ci	dev_warn(mlxsw_sp->bus_info->dev, "FIB abort triggered. Note that FIB entries are no longer being offloaded to this device.\n");
59008c2ecf20Sopenharmony_ci	mlxsw_sp_router_fib_flush(mlxsw_sp);
59018c2ecf20Sopenharmony_ci	mlxsw_sp->router->aborted = true;
59028c2ecf20Sopenharmony_ci	err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
59038c2ecf20Sopenharmony_ci	if (err)
59048c2ecf20Sopenharmony_ci		dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
59058c2ecf20Sopenharmony_ci}
59068c2ecf20Sopenharmony_ci
59078c2ecf20Sopenharmony_cistruct mlxsw_sp_fib6_event_work {
59088c2ecf20Sopenharmony_ci	struct fib6_info **rt_arr;
59098c2ecf20Sopenharmony_ci	unsigned int nrt6;
59108c2ecf20Sopenharmony_ci};
59118c2ecf20Sopenharmony_ci
59128c2ecf20Sopenharmony_cistruct mlxsw_sp_fib_event_work {
59138c2ecf20Sopenharmony_ci	struct work_struct work;
59148c2ecf20Sopenharmony_ci	union {
59158c2ecf20Sopenharmony_ci		struct mlxsw_sp_fib6_event_work fib6_work;
59168c2ecf20Sopenharmony_ci		struct fib_entry_notifier_info fen_info;
59178c2ecf20Sopenharmony_ci		struct fib_rule_notifier_info fr_info;
59188c2ecf20Sopenharmony_ci		struct fib_nh_notifier_info fnh_info;
59198c2ecf20Sopenharmony_ci		struct mfc_entry_notifier_info men_info;
59208c2ecf20Sopenharmony_ci		struct vif_entry_notifier_info ven_info;
59218c2ecf20Sopenharmony_ci	};
59228c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
59238c2ecf20Sopenharmony_ci	unsigned long event;
59248c2ecf20Sopenharmony_ci};
59258c2ecf20Sopenharmony_ci
59268c2ecf20Sopenharmony_cistatic int
59278c2ecf20Sopenharmony_cimlxsw_sp_router_fib6_work_init(struct mlxsw_sp_fib6_event_work *fib6_work,
59288c2ecf20Sopenharmony_ci			       struct fib6_entry_notifier_info *fen6_info)
59298c2ecf20Sopenharmony_ci{
59308c2ecf20Sopenharmony_ci	struct fib6_info *rt = fen6_info->rt;
59318c2ecf20Sopenharmony_ci	struct fib6_info **rt_arr;
59328c2ecf20Sopenharmony_ci	struct fib6_info *iter;
59338c2ecf20Sopenharmony_ci	unsigned int nrt6;
59348c2ecf20Sopenharmony_ci	int i = 0;
59358c2ecf20Sopenharmony_ci
59368c2ecf20Sopenharmony_ci	nrt6 = fen6_info->nsiblings + 1;
59378c2ecf20Sopenharmony_ci
59388c2ecf20Sopenharmony_ci	rt_arr = kcalloc(nrt6, sizeof(struct fib6_info *), GFP_ATOMIC);
59398c2ecf20Sopenharmony_ci	if (!rt_arr)
59408c2ecf20Sopenharmony_ci		return -ENOMEM;
59418c2ecf20Sopenharmony_ci
59428c2ecf20Sopenharmony_ci	fib6_work->rt_arr = rt_arr;
59438c2ecf20Sopenharmony_ci	fib6_work->nrt6 = nrt6;
59448c2ecf20Sopenharmony_ci
59458c2ecf20Sopenharmony_ci	rt_arr[0] = rt;
59468c2ecf20Sopenharmony_ci	fib6_info_hold(rt);
59478c2ecf20Sopenharmony_ci
59488c2ecf20Sopenharmony_ci	if (!fen6_info->nsiblings)
59498c2ecf20Sopenharmony_ci		return 0;
59508c2ecf20Sopenharmony_ci
59518c2ecf20Sopenharmony_ci	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
59528c2ecf20Sopenharmony_ci		if (i == fen6_info->nsiblings)
59538c2ecf20Sopenharmony_ci			break;
59548c2ecf20Sopenharmony_ci
59558c2ecf20Sopenharmony_ci		rt_arr[i + 1] = iter;
59568c2ecf20Sopenharmony_ci		fib6_info_hold(iter);
59578c2ecf20Sopenharmony_ci		i++;
59588c2ecf20Sopenharmony_ci	}
59598c2ecf20Sopenharmony_ci	WARN_ON_ONCE(i != fen6_info->nsiblings);
59608c2ecf20Sopenharmony_ci
59618c2ecf20Sopenharmony_ci	return 0;
59628c2ecf20Sopenharmony_ci}
59638c2ecf20Sopenharmony_ci
59648c2ecf20Sopenharmony_cistatic void
59658c2ecf20Sopenharmony_cimlxsw_sp_router_fib6_work_fini(struct mlxsw_sp_fib6_event_work *fib6_work)
59668c2ecf20Sopenharmony_ci{
59678c2ecf20Sopenharmony_ci	int i;
59688c2ecf20Sopenharmony_ci
59698c2ecf20Sopenharmony_ci	for (i = 0; i < fib6_work->nrt6; i++)
59708c2ecf20Sopenharmony_ci		mlxsw_sp_rt6_release(fib6_work->rt_arr[i]);
59718c2ecf20Sopenharmony_ci	kfree(fib6_work->rt_arr);
59728c2ecf20Sopenharmony_ci}
59738c2ecf20Sopenharmony_ci
59748c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
59758c2ecf20Sopenharmony_ci{
59768c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_event_work *fib_work =
59778c2ecf20Sopenharmony_ci		container_of(work, struct mlxsw_sp_fib_event_work, work);
59788c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
59798c2ecf20Sopenharmony_ci	int err;
59808c2ecf20Sopenharmony_ci
59818c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
59828c2ecf20Sopenharmony_ci	mlxsw_sp_span_respin(mlxsw_sp);
59838c2ecf20Sopenharmony_ci
59848c2ecf20Sopenharmony_ci	switch (fib_work->event) {
59858c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_REPLACE:
59868c2ecf20Sopenharmony_ci		err = mlxsw_sp_router_fib4_replace(mlxsw_sp,
59878c2ecf20Sopenharmony_ci						   &fib_work->fen_info);
59888c2ecf20Sopenharmony_ci		if (err)
59898c2ecf20Sopenharmony_ci			mlxsw_sp_router_fib_abort(mlxsw_sp);
59908c2ecf20Sopenharmony_ci		fib_info_put(fib_work->fen_info.fi);
59918c2ecf20Sopenharmony_ci		break;
59928c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_DEL:
59938c2ecf20Sopenharmony_ci		mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
59948c2ecf20Sopenharmony_ci		fib_info_put(fib_work->fen_info.fi);
59958c2ecf20Sopenharmony_ci		break;
59968c2ecf20Sopenharmony_ci	case FIB_EVENT_NH_ADD:
59978c2ecf20Sopenharmony_ci	case FIB_EVENT_NH_DEL:
59988c2ecf20Sopenharmony_ci		mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
59998c2ecf20Sopenharmony_ci					fib_work->fnh_info.fib_nh);
60008c2ecf20Sopenharmony_ci		fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
60018c2ecf20Sopenharmony_ci		break;
60028c2ecf20Sopenharmony_ci	}
60038c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
60048c2ecf20Sopenharmony_ci	kfree(fib_work);
60058c2ecf20Sopenharmony_ci}
60068c2ecf20Sopenharmony_ci
60078c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
60088c2ecf20Sopenharmony_ci{
60098c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_event_work *fib_work =
60108c2ecf20Sopenharmony_ci		container_of(work, struct mlxsw_sp_fib_event_work, work);
60118c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
60128c2ecf20Sopenharmony_ci	int err;
60138c2ecf20Sopenharmony_ci
60148c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
60158c2ecf20Sopenharmony_ci	mlxsw_sp_span_respin(mlxsw_sp);
60168c2ecf20Sopenharmony_ci
60178c2ecf20Sopenharmony_ci	switch (fib_work->event) {
60188c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_REPLACE:
60198c2ecf20Sopenharmony_ci		err = mlxsw_sp_router_fib6_replace(mlxsw_sp,
60208c2ecf20Sopenharmony_ci						   fib_work->fib6_work.rt_arr,
60218c2ecf20Sopenharmony_ci						   fib_work->fib6_work.nrt6);
60228c2ecf20Sopenharmony_ci		if (err)
60238c2ecf20Sopenharmony_ci			mlxsw_sp_router_fib_abort(mlxsw_sp);
60248c2ecf20Sopenharmony_ci		mlxsw_sp_router_fib6_work_fini(&fib_work->fib6_work);
60258c2ecf20Sopenharmony_ci		break;
60268c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_APPEND:
60278c2ecf20Sopenharmony_ci		err = mlxsw_sp_router_fib6_append(mlxsw_sp,
60288c2ecf20Sopenharmony_ci						  fib_work->fib6_work.rt_arr,
60298c2ecf20Sopenharmony_ci						  fib_work->fib6_work.nrt6);
60308c2ecf20Sopenharmony_ci		if (err)
60318c2ecf20Sopenharmony_ci			mlxsw_sp_router_fib_abort(mlxsw_sp);
60328c2ecf20Sopenharmony_ci		mlxsw_sp_router_fib6_work_fini(&fib_work->fib6_work);
60338c2ecf20Sopenharmony_ci		break;
60348c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_DEL:
60358c2ecf20Sopenharmony_ci		mlxsw_sp_router_fib6_del(mlxsw_sp,
60368c2ecf20Sopenharmony_ci					 fib_work->fib6_work.rt_arr,
60378c2ecf20Sopenharmony_ci					 fib_work->fib6_work.nrt6);
60388c2ecf20Sopenharmony_ci		mlxsw_sp_router_fib6_work_fini(&fib_work->fib6_work);
60398c2ecf20Sopenharmony_ci		break;
60408c2ecf20Sopenharmony_ci	}
60418c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
60428c2ecf20Sopenharmony_ci	kfree(fib_work);
60438c2ecf20Sopenharmony_ci}
60448c2ecf20Sopenharmony_ci
60458c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_fibmr_event_work(struct work_struct *work)
60468c2ecf20Sopenharmony_ci{
60478c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_event_work *fib_work =
60488c2ecf20Sopenharmony_ci		container_of(work, struct mlxsw_sp_fib_event_work, work);
60498c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
60508c2ecf20Sopenharmony_ci	bool replace;
60518c2ecf20Sopenharmony_ci	int err;
60528c2ecf20Sopenharmony_ci
60538c2ecf20Sopenharmony_ci	rtnl_lock();
60548c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
60558c2ecf20Sopenharmony_ci	switch (fib_work->event) {
60568c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_REPLACE:
60578c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_ADD:
60588c2ecf20Sopenharmony_ci		replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
60598c2ecf20Sopenharmony_ci
60608c2ecf20Sopenharmony_ci		err = mlxsw_sp_router_fibmr_add(mlxsw_sp, &fib_work->men_info,
60618c2ecf20Sopenharmony_ci						replace);
60628c2ecf20Sopenharmony_ci		if (err)
60638c2ecf20Sopenharmony_ci			mlxsw_sp_router_fib_abort(mlxsw_sp);
60648c2ecf20Sopenharmony_ci		mr_cache_put(fib_work->men_info.mfc);
60658c2ecf20Sopenharmony_ci		break;
60668c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_DEL:
60678c2ecf20Sopenharmony_ci		mlxsw_sp_router_fibmr_del(mlxsw_sp, &fib_work->men_info);
60688c2ecf20Sopenharmony_ci		mr_cache_put(fib_work->men_info.mfc);
60698c2ecf20Sopenharmony_ci		break;
60708c2ecf20Sopenharmony_ci	case FIB_EVENT_VIF_ADD:
60718c2ecf20Sopenharmony_ci		err = mlxsw_sp_router_fibmr_vif_add(mlxsw_sp,
60728c2ecf20Sopenharmony_ci						    &fib_work->ven_info);
60738c2ecf20Sopenharmony_ci		if (err)
60748c2ecf20Sopenharmony_ci			mlxsw_sp_router_fib_abort(mlxsw_sp);
60758c2ecf20Sopenharmony_ci		dev_put(fib_work->ven_info.dev);
60768c2ecf20Sopenharmony_ci		break;
60778c2ecf20Sopenharmony_ci	case FIB_EVENT_VIF_DEL:
60788c2ecf20Sopenharmony_ci		mlxsw_sp_router_fibmr_vif_del(mlxsw_sp,
60798c2ecf20Sopenharmony_ci					      &fib_work->ven_info);
60808c2ecf20Sopenharmony_ci		dev_put(fib_work->ven_info.dev);
60818c2ecf20Sopenharmony_ci		break;
60828c2ecf20Sopenharmony_ci	}
60838c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
60848c2ecf20Sopenharmony_ci	rtnl_unlock();
60858c2ecf20Sopenharmony_ci	kfree(fib_work);
60868c2ecf20Sopenharmony_ci}
60878c2ecf20Sopenharmony_ci
60888c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
60898c2ecf20Sopenharmony_ci				       struct fib_notifier_info *info)
60908c2ecf20Sopenharmony_ci{
60918c2ecf20Sopenharmony_ci	struct fib_entry_notifier_info *fen_info;
60928c2ecf20Sopenharmony_ci	struct fib_nh_notifier_info *fnh_info;
60938c2ecf20Sopenharmony_ci
60948c2ecf20Sopenharmony_ci	switch (fib_work->event) {
60958c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_REPLACE:
60968c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_DEL:
60978c2ecf20Sopenharmony_ci		fen_info = container_of(info, struct fib_entry_notifier_info,
60988c2ecf20Sopenharmony_ci					info);
60998c2ecf20Sopenharmony_ci		fib_work->fen_info = *fen_info;
61008c2ecf20Sopenharmony_ci		/* Take reference on fib_info to prevent it from being
61018c2ecf20Sopenharmony_ci		 * freed while work is queued. Release it afterwards.
61028c2ecf20Sopenharmony_ci		 */
61038c2ecf20Sopenharmony_ci		fib_info_hold(fib_work->fen_info.fi);
61048c2ecf20Sopenharmony_ci		break;
61058c2ecf20Sopenharmony_ci	case FIB_EVENT_NH_ADD:
61068c2ecf20Sopenharmony_ci	case FIB_EVENT_NH_DEL:
61078c2ecf20Sopenharmony_ci		fnh_info = container_of(info, struct fib_nh_notifier_info,
61088c2ecf20Sopenharmony_ci					info);
61098c2ecf20Sopenharmony_ci		fib_work->fnh_info = *fnh_info;
61108c2ecf20Sopenharmony_ci		fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
61118c2ecf20Sopenharmony_ci		break;
61128c2ecf20Sopenharmony_ci	}
61138c2ecf20Sopenharmony_ci}
61148c2ecf20Sopenharmony_ci
61158c2ecf20Sopenharmony_cistatic int mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
61168c2ecf20Sopenharmony_ci				      struct fib_notifier_info *info)
61178c2ecf20Sopenharmony_ci{
61188c2ecf20Sopenharmony_ci	struct fib6_entry_notifier_info *fen6_info;
61198c2ecf20Sopenharmony_ci	int err;
61208c2ecf20Sopenharmony_ci
61218c2ecf20Sopenharmony_ci	switch (fib_work->event) {
61228c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_REPLACE:
61238c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_APPEND:
61248c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_DEL:
61258c2ecf20Sopenharmony_ci		fen6_info = container_of(info, struct fib6_entry_notifier_info,
61268c2ecf20Sopenharmony_ci					 info);
61278c2ecf20Sopenharmony_ci		err = mlxsw_sp_router_fib6_work_init(&fib_work->fib6_work,
61288c2ecf20Sopenharmony_ci						     fen6_info);
61298c2ecf20Sopenharmony_ci		if (err)
61308c2ecf20Sopenharmony_ci			return err;
61318c2ecf20Sopenharmony_ci		break;
61328c2ecf20Sopenharmony_ci	}
61338c2ecf20Sopenharmony_ci
61348c2ecf20Sopenharmony_ci	return 0;
61358c2ecf20Sopenharmony_ci}
61368c2ecf20Sopenharmony_ci
61378c2ecf20Sopenharmony_cistatic void
61388c2ecf20Sopenharmony_cimlxsw_sp_router_fibmr_event(struct mlxsw_sp_fib_event_work *fib_work,
61398c2ecf20Sopenharmony_ci			    struct fib_notifier_info *info)
61408c2ecf20Sopenharmony_ci{
61418c2ecf20Sopenharmony_ci	switch (fib_work->event) {
61428c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_REPLACE:
61438c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_ADD:
61448c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_DEL:
61458c2ecf20Sopenharmony_ci		memcpy(&fib_work->men_info, info, sizeof(fib_work->men_info));
61468c2ecf20Sopenharmony_ci		mr_cache_hold(fib_work->men_info.mfc);
61478c2ecf20Sopenharmony_ci		break;
61488c2ecf20Sopenharmony_ci	case FIB_EVENT_VIF_ADD:
61498c2ecf20Sopenharmony_ci	case FIB_EVENT_VIF_DEL:
61508c2ecf20Sopenharmony_ci		memcpy(&fib_work->ven_info, info, sizeof(fib_work->ven_info));
61518c2ecf20Sopenharmony_ci		dev_hold(fib_work->ven_info.dev);
61528c2ecf20Sopenharmony_ci		break;
61538c2ecf20Sopenharmony_ci	}
61548c2ecf20Sopenharmony_ci}
61558c2ecf20Sopenharmony_ci
61568c2ecf20Sopenharmony_cistatic int mlxsw_sp_router_fib_rule_event(unsigned long event,
61578c2ecf20Sopenharmony_ci					  struct fib_notifier_info *info,
61588c2ecf20Sopenharmony_ci					  struct mlxsw_sp *mlxsw_sp)
61598c2ecf20Sopenharmony_ci{
61608c2ecf20Sopenharmony_ci	struct netlink_ext_ack *extack = info->extack;
61618c2ecf20Sopenharmony_ci	struct fib_rule_notifier_info *fr_info;
61628c2ecf20Sopenharmony_ci	struct fib_rule *rule;
61638c2ecf20Sopenharmony_ci	int err = 0;
61648c2ecf20Sopenharmony_ci
61658c2ecf20Sopenharmony_ci	/* nothing to do at the moment */
61668c2ecf20Sopenharmony_ci	if (event == FIB_EVENT_RULE_DEL)
61678c2ecf20Sopenharmony_ci		return 0;
61688c2ecf20Sopenharmony_ci
61698c2ecf20Sopenharmony_ci	if (mlxsw_sp->router->aborted)
61708c2ecf20Sopenharmony_ci		return 0;
61718c2ecf20Sopenharmony_ci
61728c2ecf20Sopenharmony_ci	fr_info = container_of(info, struct fib_rule_notifier_info, info);
61738c2ecf20Sopenharmony_ci	rule = fr_info->rule;
61748c2ecf20Sopenharmony_ci
61758c2ecf20Sopenharmony_ci	/* Rule only affects locally generated traffic */
61768c2ecf20Sopenharmony_ci	if (rule->iifindex == mlxsw_sp_net(mlxsw_sp)->loopback_dev->ifindex)
61778c2ecf20Sopenharmony_ci		return 0;
61788c2ecf20Sopenharmony_ci
61798c2ecf20Sopenharmony_ci	switch (info->family) {
61808c2ecf20Sopenharmony_ci	case AF_INET:
61818c2ecf20Sopenharmony_ci		if (!fib4_rule_default(rule) && !rule->l3mdev)
61828c2ecf20Sopenharmony_ci			err = -EOPNOTSUPP;
61838c2ecf20Sopenharmony_ci		break;
61848c2ecf20Sopenharmony_ci	case AF_INET6:
61858c2ecf20Sopenharmony_ci		if (!fib6_rule_default(rule) && !rule->l3mdev)
61868c2ecf20Sopenharmony_ci			err = -EOPNOTSUPP;
61878c2ecf20Sopenharmony_ci		break;
61888c2ecf20Sopenharmony_ci	case RTNL_FAMILY_IPMR:
61898c2ecf20Sopenharmony_ci		if (!ipmr_rule_default(rule) && !rule->l3mdev)
61908c2ecf20Sopenharmony_ci			err = -EOPNOTSUPP;
61918c2ecf20Sopenharmony_ci		break;
61928c2ecf20Sopenharmony_ci	case RTNL_FAMILY_IP6MR:
61938c2ecf20Sopenharmony_ci		if (!ip6mr_rule_default(rule) && !rule->l3mdev)
61948c2ecf20Sopenharmony_ci			err = -EOPNOTSUPP;
61958c2ecf20Sopenharmony_ci		break;
61968c2ecf20Sopenharmony_ci	}
61978c2ecf20Sopenharmony_ci
61988c2ecf20Sopenharmony_ci	if (err < 0)
61998c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "FIB rules not supported");
62008c2ecf20Sopenharmony_ci
62018c2ecf20Sopenharmony_ci	return err;
62028c2ecf20Sopenharmony_ci}
62038c2ecf20Sopenharmony_ci
62048c2ecf20Sopenharmony_ci/* Called with rcu_read_lock() */
62058c2ecf20Sopenharmony_cistatic int mlxsw_sp_router_fib_event(struct notifier_block *nb,
62068c2ecf20Sopenharmony_ci				     unsigned long event, void *ptr)
62078c2ecf20Sopenharmony_ci{
62088c2ecf20Sopenharmony_ci	struct mlxsw_sp_fib_event_work *fib_work;
62098c2ecf20Sopenharmony_ci	struct fib_notifier_info *info = ptr;
62108c2ecf20Sopenharmony_ci	struct mlxsw_sp_router *router;
62118c2ecf20Sopenharmony_ci	int err;
62128c2ecf20Sopenharmony_ci
62138c2ecf20Sopenharmony_ci	if ((info->family != AF_INET && info->family != AF_INET6 &&
62148c2ecf20Sopenharmony_ci	     info->family != RTNL_FAMILY_IPMR &&
62158c2ecf20Sopenharmony_ci	     info->family != RTNL_FAMILY_IP6MR))
62168c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
62178c2ecf20Sopenharmony_ci
62188c2ecf20Sopenharmony_ci	router = container_of(nb, struct mlxsw_sp_router, fib_nb);
62198c2ecf20Sopenharmony_ci
62208c2ecf20Sopenharmony_ci	switch (event) {
62218c2ecf20Sopenharmony_ci	case FIB_EVENT_RULE_ADD:
62228c2ecf20Sopenharmony_ci	case FIB_EVENT_RULE_DEL:
62238c2ecf20Sopenharmony_ci		err = mlxsw_sp_router_fib_rule_event(event, info,
62248c2ecf20Sopenharmony_ci						     router->mlxsw_sp);
62258c2ecf20Sopenharmony_ci		return notifier_from_errno(err);
62268c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_ADD:
62278c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_REPLACE:
62288c2ecf20Sopenharmony_ci	case FIB_EVENT_ENTRY_APPEND:
62298c2ecf20Sopenharmony_ci		if (router->aborted) {
62308c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(info->extack, "FIB offload was aborted. Not configuring route");
62318c2ecf20Sopenharmony_ci			return notifier_from_errno(-EINVAL);
62328c2ecf20Sopenharmony_ci		}
62338c2ecf20Sopenharmony_ci		if (info->family == AF_INET) {
62348c2ecf20Sopenharmony_ci			struct fib_entry_notifier_info *fen_info = ptr;
62358c2ecf20Sopenharmony_ci
62368c2ecf20Sopenharmony_ci			if (fen_info->fi->fib_nh_is_v6) {
62378c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(info->extack, "IPv6 gateway with IPv4 route is not supported");
62388c2ecf20Sopenharmony_ci				return notifier_from_errno(-EINVAL);
62398c2ecf20Sopenharmony_ci			}
62408c2ecf20Sopenharmony_ci			if (fen_info->fi->nh) {
62418c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(info->extack, "IPv4 route with nexthop objects is not supported");
62428c2ecf20Sopenharmony_ci				return notifier_from_errno(-EINVAL);
62438c2ecf20Sopenharmony_ci			}
62448c2ecf20Sopenharmony_ci		} else if (info->family == AF_INET6) {
62458c2ecf20Sopenharmony_ci			struct fib6_entry_notifier_info *fen6_info;
62468c2ecf20Sopenharmony_ci
62478c2ecf20Sopenharmony_ci			fen6_info = container_of(info,
62488c2ecf20Sopenharmony_ci						 struct fib6_entry_notifier_info,
62498c2ecf20Sopenharmony_ci						 info);
62508c2ecf20Sopenharmony_ci			if (fen6_info->rt->nh) {
62518c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(info->extack, "IPv6 route with nexthop objects is not supported");
62528c2ecf20Sopenharmony_ci				return notifier_from_errno(-EINVAL);
62538c2ecf20Sopenharmony_ci			}
62548c2ecf20Sopenharmony_ci		}
62558c2ecf20Sopenharmony_ci		break;
62568c2ecf20Sopenharmony_ci	}
62578c2ecf20Sopenharmony_ci
62588c2ecf20Sopenharmony_ci	fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
62598c2ecf20Sopenharmony_ci	if (!fib_work)
62608c2ecf20Sopenharmony_ci		return NOTIFY_BAD;
62618c2ecf20Sopenharmony_ci
62628c2ecf20Sopenharmony_ci	fib_work->mlxsw_sp = router->mlxsw_sp;
62638c2ecf20Sopenharmony_ci	fib_work->event = event;
62648c2ecf20Sopenharmony_ci
62658c2ecf20Sopenharmony_ci	switch (info->family) {
62668c2ecf20Sopenharmony_ci	case AF_INET:
62678c2ecf20Sopenharmony_ci		INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
62688c2ecf20Sopenharmony_ci		mlxsw_sp_router_fib4_event(fib_work, info);
62698c2ecf20Sopenharmony_ci		break;
62708c2ecf20Sopenharmony_ci	case AF_INET6:
62718c2ecf20Sopenharmony_ci		INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
62728c2ecf20Sopenharmony_ci		err = mlxsw_sp_router_fib6_event(fib_work, info);
62738c2ecf20Sopenharmony_ci		if (err)
62748c2ecf20Sopenharmony_ci			goto err_fib_event;
62758c2ecf20Sopenharmony_ci		break;
62768c2ecf20Sopenharmony_ci	case RTNL_FAMILY_IP6MR:
62778c2ecf20Sopenharmony_ci	case RTNL_FAMILY_IPMR:
62788c2ecf20Sopenharmony_ci		INIT_WORK(&fib_work->work, mlxsw_sp_router_fibmr_event_work);
62798c2ecf20Sopenharmony_ci		mlxsw_sp_router_fibmr_event(fib_work, info);
62808c2ecf20Sopenharmony_ci		break;
62818c2ecf20Sopenharmony_ci	}
62828c2ecf20Sopenharmony_ci
62838c2ecf20Sopenharmony_ci	mlxsw_core_schedule_work(&fib_work->work);
62848c2ecf20Sopenharmony_ci
62858c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
62868c2ecf20Sopenharmony_ci
62878c2ecf20Sopenharmony_cierr_fib_event:
62888c2ecf20Sopenharmony_ci	kfree(fib_work);
62898c2ecf20Sopenharmony_ci	return NOTIFY_BAD;
62908c2ecf20Sopenharmony_ci}
62918c2ecf20Sopenharmony_ci
62928c2ecf20Sopenharmony_cistatic struct mlxsw_sp_rif *
62938c2ecf20Sopenharmony_cimlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
62948c2ecf20Sopenharmony_ci			 const struct net_device *dev)
62958c2ecf20Sopenharmony_ci{
62968c2ecf20Sopenharmony_ci	int i;
62978c2ecf20Sopenharmony_ci
62988c2ecf20Sopenharmony_ci	for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
62998c2ecf20Sopenharmony_ci		if (mlxsw_sp->router->rifs[i] &&
63008c2ecf20Sopenharmony_ci		    mlxsw_sp->router->rifs[i]->dev == dev)
63018c2ecf20Sopenharmony_ci			return mlxsw_sp->router->rifs[i];
63028c2ecf20Sopenharmony_ci
63038c2ecf20Sopenharmony_ci	return NULL;
63048c2ecf20Sopenharmony_ci}
63058c2ecf20Sopenharmony_ci
63068c2ecf20Sopenharmony_cibool mlxsw_sp_rif_exists(struct mlxsw_sp *mlxsw_sp,
63078c2ecf20Sopenharmony_ci			 const struct net_device *dev)
63088c2ecf20Sopenharmony_ci{
63098c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
63108c2ecf20Sopenharmony_ci
63118c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
63128c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
63138c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
63148c2ecf20Sopenharmony_ci
63158c2ecf20Sopenharmony_ci	return rif;
63168c2ecf20Sopenharmony_ci}
63178c2ecf20Sopenharmony_ci
63188c2ecf20Sopenharmony_ciu16 mlxsw_sp_rif_vid(struct mlxsw_sp *mlxsw_sp, const struct net_device *dev)
63198c2ecf20Sopenharmony_ci{
63208c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
63218c2ecf20Sopenharmony_ci	u16 vid = 0;
63228c2ecf20Sopenharmony_ci
63238c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
63248c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
63258c2ecf20Sopenharmony_ci	if (!rif)
63268c2ecf20Sopenharmony_ci		goto out;
63278c2ecf20Sopenharmony_ci
63288c2ecf20Sopenharmony_ci	/* We only return the VID for VLAN RIFs. Otherwise we return an
63298c2ecf20Sopenharmony_ci	 * invalid value (0).
63308c2ecf20Sopenharmony_ci	 */
63318c2ecf20Sopenharmony_ci	if (rif->ops->type != MLXSW_SP_RIF_TYPE_VLAN)
63328c2ecf20Sopenharmony_ci		goto out;
63338c2ecf20Sopenharmony_ci
63348c2ecf20Sopenharmony_ci	vid = mlxsw_sp_fid_8021q_vid(rif->fid);
63358c2ecf20Sopenharmony_ci
63368c2ecf20Sopenharmony_ciout:
63378c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
63388c2ecf20Sopenharmony_ci	return vid;
63398c2ecf20Sopenharmony_ci}
63408c2ecf20Sopenharmony_ci
63418c2ecf20Sopenharmony_cistatic int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
63428c2ecf20Sopenharmony_ci{
63438c2ecf20Sopenharmony_ci	char ritr_pl[MLXSW_REG_RITR_LEN];
63448c2ecf20Sopenharmony_ci	int err;
63458c2ecf20Sopenharmony_ci
63468c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
63478c2ecf20Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
63488c2ecf20Sopenharmony_ci	if (err)
63498c2ecf20Sopenharmony_ci		return err;
63508c2ecf20Sopenharmony_ci
63518c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_enable_set(ritr_pl, false);
63528c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
63538c2ecf20Sopenharmony_ci}
63548c2ecf20Sopenharmony_ci
63558c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
63568c2ecf20Sopenharmony_ci					  struct mlxsw_sp_rif *rif)
63578c2ecf20Sopenharmony_ci{
63588c2ecf20Sopenharmony_ci	mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
63598c2ecf20Sopenharmony_ci	mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
63608c2ecf20Sopenharmony_ci	mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
63618c2ecf20Sopenharmony_ci}
63628c2ecf20Sopenharmony_ci
63638c2ecf20Sopenharmony_cistatic bool
63648c2ecf20Sopenharmony_cimlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
63658c2ecf20Sopenharmony_ci			   unsigned long event)
63668c2ecf20Sopenharmony_ci{
63678c2ecf20Sopenharmony_ci	struct inet6_dev *inet6_dev;
63688c2ecf20Sopenharmony_ci	bool addr_list_empty = true;
63698c2ecf20Sopenharmony_ci	struct in_device *idev;
63708c2ecf20Sopenharmony_ci
63718c2ecf20Sopenharmony_ci	switch (event) {
63728c2ecf20Sopenharmony_ci	case NETDEV_UP:
63738c2ecf20Sopenharmony_ci		return rif == NULL;
63748c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
63758c2ecf20Sopenharmony_ci		rcu_read_lock();
63768c2ecf20Sopenharmony_ci		idev = __in_dev_get_rcu(dev);
63778c2ecf20Sopenharmony_ci		if (idev && idev->ifa_list)
63788c2ecf20Sopenharmony_ci			addr_list_empty = false;
63798c2ecf20Sopenharmony_ci
63808c2ecf20Sopenharmony_ci		inet6_dev = __in6_dev_get(dev);
63818c2ecf20Sopenharmony_ci		if (addr_list_empty && inet6_dev &&
63828c2ecf20Sopenharmony_ci		    !list_empty(&inet6_dev->addr_list))
63838c2ecf20Sopenharmony_ci			addr_list_empty = false;
63848c2ecf20Sopenharmony_ci		rcu_read_unlock();
63858c2ecf20Sopenharmony_ci
63868c2ecf20Sopenharmony_ci		/* macvlans do not have a RIF, but rather piggy back on the
63878c2ecf20Sopenharmony_ci		 * RIF of their lower device.
63888c2ecf20Sopenharmony_ci		 */
63898c2ecf20Sopenharmony_ci		if (netif_is_macvlan(dev) && addr_list_empty)
63908c2ecf20Sopenharmony_ci			return true;
63918c2ecf20Sopenharmony_ci
63928c2ecf20Sopenharmony_ci		if (rif && addr_list_empty &&
63938c2ecf20Sopenharmony_ci		    !netif_is_l3_slave(rif->dev))
63948c2ecf20Sopenharmony_ci			return true;
63958c2ecf20Sopenharmony_ci		/* It is possible we already removed the RIF ourselves
63968c2ecf20Sopenharmony_ci		 * if it was assigned to a netdev that is now a bridge
63978c2ecf20Sopenharmony_ci		 * or LAG slave.
63988c2ecf20Sopenharmony_ci		 */
63998c2ecf20Sopenharmony_ci		return false;
64008c2ecf20Sopenharmony_ci	}
64018c2ecf20Sopenharmony_ci
64028c2ecf20Sopenharmony_ci	return false;
64038c2ecf20Sopenharmony_ci}
64048c2ecf20Sopenharmony_ci
64058c2ecf20Sopenharmony_cistatic enum mlxsw_sp_rif_type
64068c2ecf20Sopenharmony_cimlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
64078c2ecf20Sopenharmony_ci		      const struct net_device *dev)
64088c2ecf20Sopenharmony_ci{
64098c2ecf20Sopenharmony_ci	enum mlxsw_sp_fid_type type;
64108c2ecf20Sopenharmony_ci
64118c2ecf20Sopenharmony_ci	if (mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL))
64128c2ecf20Sopenharmony_ci		return MLXSW_SP_RIF_TYPE_IPIP_LB;
64138c2ecf20Sopenharmony_ci
64148c2ecf20Sopenharmony_ci	/* Otherwise RIF type is derived from the type of the underlying FID. */
64158c2ecf20Sopenharmony_ci	if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
64168c2ecf20Sopenharmony_ci		type = MLXSW_SP_FID_TYPE_8021Q;
64178c2ecf20Sopenharmony_ci	else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
64188c2ecf20Sopenharmony_ci		type = MLXSW_SP_FID_TYPE_8021Q;
64198c2ecf20Sopenharmony_ci	else if (netif_is_bridge_master(dev))
64208c2ecf20Sopenharmony_ci		type = MLXSW_SP_FID_TYPE_8021D;
64218c2ecf20Sopenharmony_ci	else
64228c2ecf20Sopenharmony_ci		type = MLXSW_SP_FID_TYPE_RFID;
64238c2ecf20Sopenharmony_ci
64248c2ecf20Sopenharmony_ci	return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
64258c2ecf20Sopenharmony_ci}
64268c2ecf20Sopenharmony_ci
64278c2ecf20Sopenharmony_cistatic int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
64288c2ecf20Sopenharmony_ci{
64298c2ecf20Sopenharmony_ci	int i;
64308c2ecf20Sopenharmony_ci
64318c2ecf20Sopenharmony_ci	for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
64328c2ecf20Sopenharmony_ci		if (!mlxsw_sp->router->rifs[i]) {
64338c2ecf20Sopenharmony_ci			*p_rif_index = i;
64348c2ecf20Sopenharmony_ci			return 0;
64358c2ecf20Sopenharmony_ci		}
64368c2ecf20Sopenharmony_ci	}
64378c2ecf20Sopenharmony_ci
64388c2ecf20Sopenharmony_ci	return -ENOBUFS;
64398c2ecf20Sopenharmony_ci}
64408c2ecf20Sopenharmony_ci
64418c2ecf20Sopenharmony_cistatic struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
64428c2ecf20Sopenharmony_ci					       u16 vr_id,
64438c2ecf20Sopenharmony_ci					       struct net_device *l3_dev)
64448c2ecf20Sopenharmony_ci{
64458c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
64468c2ecf20Sopenharmony_ci
64478c2ecf20Sopenharmony_ci	rif = kzalloc(rif_size, GFP_KERNEL);
64488c2ecf20Sopenharmony_ci	if (!rif)
64498c2ecf20Sopenharmony_ci		return NULL;
64508c2ecf20Sopenharmony_ci
64518c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&rif->nexthop_list);
64528c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&rif->neigh_list);
64538c2ecf20Sopenharmony_ci	if (l3_dev) {
64548c2ecf20Sopenharmony_ci		ether_addr_copy(rif->addr, l3_dev->dev_addr);
64558c2ecf20Sopenharmony_ci		rif->mtu = l3_dev->mtu;
64568c2ecf20Sopenharmony_ci		rif->dev = l3_dev;
64578c2ecf20Sopenharmony_ci	}
64588c2ecf20Sopenharmony_ci	rif->vr_id = vr_id;
64598c2ecf20Sopenharmony_ci	rif->rif_index = rif_index;
64608c2ecf20Sopenharmony_ci
64618c2ecf20Sopenharmony_ci	return rif;
64628c2ecf20Sopenharmony_ci}
64638c2ecf20Sopenharmony_ci
64648c2ecf20Sopenharmony_cistruct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
64658c2ecf20Sopenharmony_ci					   u16 rif_index)
64668c2ecf20Sopenharmony_ci{
64678c2ecf20Sopenharmony_ci	return mlxsw_sp->router->rifs[rif_index];
64688c2ecf20Sopenharmony_ci}
64698c2ecf20Sopenharmony_ci
64708c2ecf20Sopenharmony_ciu16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
64718c2ecf20Sopenharmony_ci{
64728c2ecf20Sopenharmony_ci	return rif->rif_index;
64738c2ecf20Sopenharmony_ci}
64748c2ecf20Sopenharmony_ci
64758c2ecf20Sopenharmony_ciu16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
64768c2ecf20Sopenharmony_ci{
64778c2ecf20Sopenharmony_ci	return lb_rif->common.rif_index;
64788c2ecf20Sopenharmony_ci}
64798c2ecf20Sopenharmony_ci
64808c2ecf20Sopenharmony_ciu16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
64818c2ecf20Sopenharmony_ci{
64828c2ecf20Sopenharmony_ci	u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(lb_rif->common.dev);
64838c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *ul_vr;
64848c2ecf20Sopenharmony_ci
64858c2ecf20Sopenharmony_ci	ul_vr = mlxsw_sp_vr_get(lb_rif->common.mlxsw_sp, ul_tb_id, NULL);
64868c2ecf20Sopenharmony_ci	if (WARN_ON(IS_ERR(ul_vr)))
64878c2ecf20Sopenharmony_ci		return 0;
64888c2ecf20Sopenharmony_ci
64898c2ecf20Sopenharmony_ci	return ul_vr->id;
64908c2ecf20Sopenharmony_ci}
64918c2ecf20Sopenharmony_ci
64928c2ecf20Sopenharmony_ciu16 mlxsw_sp_ipip_lb_ul_rif_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
64938c2ecf20Sopenharmony_ci{
64948c2ecf20Sopenharmony_ci	return lb_rif->ul_rif_id;
64958c2ecf20Sopenharmony_ci}
64968c2ecf20Sopenharmony_ci
64978c2ecf20Sopenharmony_ciint mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
64988c2ecf20Sopenharmony_ci{
64998c2ecf20Sopenharmony_ci	return rif->dev->ifindex;
65008c2ecf20Sopenharmony_ci}
65018c2ecf20Sopenharmony_ci
65028c2ecf20Sopenharmony_ciconst struct net_device *mlxsw_sp_rif_dev(const struct mlxsw_sp_rif *rif)
65038c2ecf20Sopenharmony_ci{
65048c2ecf20Sopenharmony_ci	return rif->dev;
65058c2ecf20Sopenharmony_ci}
65068c2ecf20Sopenharmony_ci
65078c2ecf20Sopenharmony_cistatic struct mlxsw_sp_rif *
65088c2ecf20Sopenharmony_cimlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
65098c2ecf20Sopenharmony_ci		    const struct mlxsw_sp_rif_params *params,
65108c2ecf20Sopenharmony_ci		    struct netlink_ext_ack *extack)
65118c2ecf20Sopenharmony_ci{
65128c2ecf20Sopenharmony_ci	u32 tb_id = l3mdev_fib_table(params->dev);
65138c2ecf20Sopenharmony_ci	const struct mlxsw_sp_rif_ops *ops;
65148c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid = NULL;
65158c2ecf20Sopenharmony_ci	enum mlxsw_sp_rif_type type;
65168c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
65178c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
65188c2ecf20Sopenharmony_ci	u16 rif_index;
65198c2ecf20Sopenharmony_ci	int i, err;
65208c2ecf20Sopenharmony_ci
65218c2ecf20Sopenharmony_ci	type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
65228c2ecf20Sopenharmony_ci	ops = mlxsw_sp->rif_ops_arr[type];
65238c2ecf20Sopenharmony_ci
65248c2ecf20Sopenharmony_ci	vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN, extack);
65258c2ecf20Sopenharmony_ci	if (IS_ERR(vr))
65268c2ecf20Sopenharmony_ci		return ERR_CAST(vr);
65278c2ecf20Sopenharmony_ci	vr->rif_count++;
65288c2ecf20Sopenharmony_ci
65298c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
65308c2ecf20Sopenharmony_ci	if (err) {
65318c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported router interfaces");
65328c2ecf20Sopenharmony_ci		goto err_rif_index_alloc;
65338c2ecf20Sopenharmony_ci	}
65348c2ecf20Sopenharmony_ci
65358c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
65368c2ecf20Sopenharmony_ci	if (!rif) {
65378c2ecf20Sopenharmony_ci		err = -ENOMEM;
65388c2ecf20Sopenharmony_ci		goto err_rif_alloc;
65398c2ecf20Sopenharmony_ci	}
65408c2ecf20Sopenharmony_ci	dev_hold(rif->dev);
65418c2ecf20Sopenharmony_ci	mlxsw_sp->router->rifs[rif_index] = rif;
65428c2ecf20Sopenharmony_ci	rif->mlxsw_sp = mlxsw_sp;
65438c2ecf20Sopenharmony_ci	rif->ops = ops;
65448c2ecf20Sopenharmony_ci
65458c2ecf20Sopenharmony_ci	if (ops->fid_get) {
65468c2ecf20Sopenharmony_ci		fid = ops->fid_get(rif, extack);
65478c2ecf20Sopenharmony_ci		if (IS_ERR(fid)) {
65488c2ecf20Sopenharmony_ci			err = PTR_ERR(fid);
65498c2ecf20Sopenharmony_ci			goto err_fid_get;
65508c2ecf20Sopenharmony_ci		}
65518c2ecf20Sopenharmony_ci		rif->fid = fid;
65528c2ecf20Sopenharmony_ci	}
65538c2ecf20Sopenharmony_ci
65548c2ecf20Sopenharmony_ci	if (ops->setup)
65558c2ecf20Sopenharmony_ci		ops->setup(rif, params);
65568c2ecf20Sopenharmony_ci
65578c2ecf20Sopenharmony_ci	err = ops->configure(rif);
65588c2ecf20Sopenharmony_ci	if (err)
65598c2ecf20Sopenharmony_ci		goto err_configure;
65608c2ecf20Sopenharmony_ci
65618c2ecf20Sopenharmony_ci	for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++) {
65628c2ecf20Sopenharmony_ci		err = mlxsw_sp_mr_rif_add(vr->mr_table[i], rif);
65638c2ecf20Sopenharmony_ci		if (err)
65648c2ecf20Sopenharmony_ci			goto err_mr_rif_add;
65658c2ecf20Sopenharmony_ci	}
65668c2ecf20Sopenharmony_ci
65678c2ecf20Sopenharmony_ci	mlxsw_sp_rif_counters_alloc(rif);
65688c2ecf20Sopenharmony_ci
65698c2ecf20Sopenharmony_ci	return rif;
65708c2ecf20Sopenharmony_ci
65718c2ecf20Sopenharmony_cierr_mr_rif_add:
65728c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--)
65738c2ecf20Sopenharmony_ci		mlxsw_sp_mr_rif_del(vr->mr_table[i], rif);
65748c2ecf20Sopenharmony_ci	ops->deconfigure(rif);
65758c2ecf20Sopenharmony_cierr_configure:
65768c2ecf20Sopenharmony_ci	if (fid)
65778c2ecf20Sopenharmony_ci		mlxsw_sp_fid_put(fid);
65788c2ecf20Sopenharmony_cierr_fid_get:
65798c2ecf20Sopenharmony_ci	mlxsw_sp->router->rifs[rif_index] = NULL;
65808c2ecf20Sopenharmony_ci	dev_put(rif->dev);
65818c2ecf20Sopenharmony_ci	kfree(rif);
65828c2ecf20Sopenharmony_cierr_rif_alloc:
65838c2ecf20Sopenharmony_cierr_rif_index_alloc:
65848c2ecf20Sopenharmony_ci	vr->rif_count--;
65858c2ecf20Sopenharmony_ci	mlxsw_sp_vr_put(mlxsw_sp, vr);
65868c2ecf20Sopenharmony_ci	return ERR_PTR(err);
65878c2ecf20Sopenharmony_ci}
65888c2ecf20Sopenharmony_ci
65898c2ecf20Sopenharmony_cistatic void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
65908c2ecf20Sopenharmony_ci{
65918c2ecf20Sopenharmony_ci	const struct mlxsw_sp_rif_ops *ops = rif->ops;
65928c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
65938c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid = rif->fid;
65948c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
65958c2ecf20Sopenharmony_ci	int i;
65968c2ecf20Sopenharmony_ci
65978c2ecf20Sopenharmony_ci	mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
65988c2ecf20Sopenharmony_ci	vr = &mlxsw_sp->router->vrs[rif->vr_id];
65998c2ecf20Sopenharmony_ci
66008c2ecf20Sopenharmony_ci	mlxsw_sp_rif_counters_free(rif);
66018c2ecf20Sopenharmony_ci	for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++)
66028c2ecf20Sopenharmony_ci		mlxsw_sp_mr_rif_del(vr->mr_table[i], rif);
66038c2ecf20Sopenharmony_ci	ops->deconfigure(rif);
66048c2ecf20Sopenharmony_ci	if (fid)
66058c2ecf20Sopenharmony_ci		/* Loopback RIFs are not associated with a FID. */
66068c2ecf20Sopenharmony_ci		mlxsw_sp_fid_put(fid);
66078c2ecf20Sopenharmony_ci	mlxsw_sp->router->rifs[rif->rif_index] = NULL;
66088c2ecf20Sopenharmony_ci	dev_put(rif->dev);
66098c2ecf20Sopenharmony_ci	kfree(rif);
66108c2ecf20Sopenharmony_ci	vr->rif_count--;
66118c2ecf20Sopenharmony_ci	mlxsw_sp_vr_put(mlxsw_sp, vr);
66128c2ecf20Sopenharmony_ci}
66138c2ecf20Sopenharmony_ci
66148c2ecf20Sopenharmony_civoid mlxsw_sp_rif_destroy_by_dev(struct mlxsw_sp *mlxsw_sp,
66158c2ecf20Sopenharmony_ci				 struct net_device *dev)
66168c2ecf20Sopenharmony_ci{
66178c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
66188c2ecf20Sopenharmony_ci
66198c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
66208c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
66218c2ecf20Sopenharmony_ci	if (!rif)
66228c2ecf20Sopenharmony_ci		goto out;
66238c2ecf20Sopenharmony_ci	mlxsw_sp_rif_destroy(rif);
66248c2ecf20Sopenharmony_ciout:
66258c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
66268c2ecf20Sopenharmony_ci}
66278c2ecf20Sopenharmony_ci
66288c2ecf20Sopenharmony_cistatic void
66298c2ecf20Sopenharmony_cimlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
66308c2ecf20Sopenharmony_ci				 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
66318c2ecf20Sopenharmony_ci{
66328c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
66338c2ecf20Sopenharmony_ci
66348c2ecf20Sopenharmony_ci	params->vid = mlxsw_sp_port_vlan->vid;
66358c2ecf20Sopenharmony_ci	params->lag = mlxsw_sp_port->lagged;
66368c2ecf20Sopenharmony_ci	if (params->lag)
66378c2ecf20Sopenharmony_ci		params->lag_id = mlxsw_sp_port->lag_id;
66388c2ecf20Sopenharmony_ci	else
66398c2ecf20Sopenharmony_ci		params->system_port = mlxsw_sp_port->local_port;
66408c2ecf20Sopenharmony_ci}
66418c2ecf20Sopenharmony_ci
66428c2ecf20Sopenharmony_cistatic struct mlxsw_sp_rif_subport *
66438c2ecf20Sopenharmony_cimlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
66448c2ecf20Sopenharmony_ci{
66458c2ecf20Sopenharmony_ci	return container_of(rif, struct mlxsw_sp_rif_subport, common);
66468c2ecf20Sopenharmony_ci}
66478c2ecf20Sopenharmony_ci
66488c2ecf20Sopenharmony_cistatic struct mlxsw_sp_rif *
66498c2ecf20Sopenharmony_cimlxsw_sp_rif_subport_get(struct mlxsw_sp *mlxsw_sp,
66508c2ecf20Sopenharmony_ci			 const struct mlxsw_sp_rif_params *params,
66518c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
66528c2ecf20Sopenharmony_ci{
66538c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_subport *rif_subport;
66548c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
66558c2ecf20Sopenharmony_ci
66568c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, params->dev);
66578c2ecf20Sopenharmony_ci	if (!rif)
66588c2ecf20Sopenharmony_ci		return mlxsw_sp_rif_create(mlxsw_sp, params, extack);
66598c2ecf20Sopenharmony_ci
66608c2ecf20Sopenharmony_ci	rif_subport = mlxsw_sp_rif_subport_rif(rif);
66618c2ecf20Sopenharmony_ci	refcount_inc(&rif_subport->ref_count);
66628c2ecf20Sopenharmony_ci	return rif;
66638c2ecf20Sopenharmony_ci}
66648c2ecf20Sopenharmony_ci
66658c2ecf20Sopenharmony_cistatic void mlxsw_sp_rif_subport_put(struct mlxsw_sp_rif *rif)
66668c2ecf20Sopenharmony_ci{
66678c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_subport *rif_subport;
66688c2ecf20Sopenharmony_ci
66698c2ecf20Sopenharmony_ci	rif_subport = mlxsw_sp_rif_subport_rif(rif);
66708c2ecf20Sopenharmony_ci	if (!refcount_dec_and_test(&rif_subport->ref_count))
66718c2ecf20Sopenharmony_ci		return;
66728c2ecf20Sopenharmony_ci
66738c2ecf20Sopenharmony_ci	mlxsw_sp_rif_destroy(rif);
66748c2ecf20Sopenharmony_ci}
66758c2ecf20Sopenharmony_ci
66768c2ecf20Sopenharmony_cistatic int
66778c2ecf20Sopenharmony_cimlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
66788c2ecf20Sopenharmony_ci			       struct net_device *l3_dev,
66798c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
66808c2ecf20Sopenharmony_ci{
66818c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
66828c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
66838c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_params params = {
66848c2ecf20Sopenharmony_ci		.dev = l3_dev,
66858c2ecf20Sopenharmony_ci	};
66868c2ecf20Sopenharmony_ci	u16 vid = mlxsw_sp_port_vlan->vid;
66878c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
66888c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid;
66898c2ecf20Sopenharmony_ci	int err;
66908c2ecf20Sopenharmony_ci
66918c2ecf20Sopenharmony_ci	mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
66928c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_subport_get(mlxsw_sp, &params, extack);
66938c2ecf20Sopenharmony_ci	if (IS_ERR(rif))
66948c2ecf20Sopenharmony_ci		return PTR_ERR(rif);
66958c2ecf20Sopenharmony_ci
66968c2ecf20Sopenharmony_ci	/* FID was already created, just take a reference */
66978c2ecf20Sopenharmony_ci	fid = rif->ops->fid_get(rif, extack);
66988c2ecf20Sopenharmony_ci	err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
66998c2ecf20Sopenharmony_ci	if (err)
67008c2ecf20Sopenharmony_ci		goto err_fid_port_vid_map;
67018c2ecf20Sopenharmony_ci
67028c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
67038c2ecf20Sopenharmony_ci	if (err)
67048c2ecf20Sopenharmony_ci		goto err_port_vid_learning_set;
67058c2ecf20Sopenharmony_ci
67068c2ecf20Sopenharmony_ci	err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
67078c2ecf20Sopenharmony_ci					BR_STATE_FORWARDING);
67088c2ecf20Sopenharmony_ci	if (err)
67098c2ecf20Sopenharmony_ci		goto err_port_vid_stp_set;
67108c2ecf20Sopenharmony_ci
67118c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan->fid = fid;
67128c2ecf20Sopenharmony_ci
67138c2ecf20Sopenharmony_ci	return 0;
67148c2ecf20Sopenharmony_ci
67158c2ecf20Sopenharmony_cierr_port_vid_stp_set:
67168c2ecf20Sopenharmony_ci	mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
67178c2ecf20Sopenharmony_cierr_port_vid_learning_set:
67188c2ecf20Sopenharmony_ci	mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
67198c2ecf20Sopenharmony_cierr_fid_port_vid_map:
67208c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
67218c2ecf20Sopenharmony_ci	mlxsw_sp_rif_subport_put(rif);
67228c2ecf20Sopenharmony_ci	return err;
67238c2ecf20Sopenharmony_ci}
67248c2ecf20Sopenharmony_ci
67258c2ecf20Sopenharmony_cistatic void
67268c2ecf20Sopenharmony_ci__mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
67278c2ecf20Sopenharmony_ci{
67288c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
67298c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
67308c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif = mlxsw_sp_fid_rif(fid);
67318c2ecf20Sopenharmony_ci	u16 vid = mlxsw_sp_port_vlan->vid;
67328c2ecf20Sopenharmony_ci
67338c2ecf20Sopenharmony_ci	if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
67348c2ecf20Sopenharmony_ci		return;
67358c2ecf20Sopenharmony_ci
67368c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan->fid = NULL;
67378c2ecf20Sopenharmony_ci	mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
67388c2ecf20Sopenharmony_ci	mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
67398c2ecf20Sopenharmony_ci	mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
67408c2ecf20Sopenharmony_ci	mlxsw_sp_fid_put(fid);
67418c2ecf20Sopenharmony_ci	mlxsw_sp_rif_subport_put(rif);
67428c2ecf20Sopenharmony_ci}
67438c2ecf20Sopenharmony_ci
67448c2ecf20Sopenharmony_civoid
67458c2ecf20Sopenharmony_cimlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
67468c2ecf20Sopenharmony_ci{
67478c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port_vlan->mlxsw_sp_port->mlxsw_sp;
67488c2ecf20Sopenharmony_ci
67498c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
67508c2ecf20Sopenharmony_ci	__mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
67518c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
67528c2ecf20Sopenharmony_ci}
67538c2ecf20Sopenharmony_ci
67548c2ecf20Sopenharmony_cistatic int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
67558c2ecf20Sopenharmony_ci					     struct net_device *port_dev,
67568c2ecf20Sopenharmony_ci					     unsigned long event, u16 vid,
67578c2ecf20Sopenharmony_ci					     struct netlink_ext_ack *extack)
67588c2ecf20Sopenharmony_ci{
67598c2ecf20Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
67608c2ecf20Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
67618c2ecf20Sopenharmony_ci
67628c2ecf20Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
67638c2ecf20Sopenharmony_ci	if (WARN_ON(!mlxsw_sp_port_vlan))
67648c2ecf20Sopenharmony_ci		return -EINVAL;
67658c2ecf20Sopenharmony_ci
67668c2ecf20Sopenharmony_ci	switch (event) {
67678c2ecf20Sopenharmony_ci	case NETDEV_UP:
67688c2ecf20Sopenharmony_ci		return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
67698c2ecf20Sopenharmony_ci						      l3_dev, extack);
67708c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
67718c2ecf20Sopenharmony_ci		__mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
67728c2ecf20Sopenharmony_ci		break;
67738c2ecf20Sopenharmony_ci	}
67748c2ecf20Sopenharmony_ci
67758c2ecf20Sopenharmony_ci	return 0;
67768c2ecf20Sopenharmony_ci}
67778c2ecf20Sopenharmony_ci
67788c2ecf20Sopenharmony_cistatic int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
67798c2ecf20Sopenharmony_ci					unsigned long event,
67808c2ecf20Sopenharmony_ci					struct netlink_ext_ack *extack)
67818c2ecf20Sopenharmony_ci{
67828c2ecf20Sopenharmony_ci	if (netif_is_bridge_port(port_dev) ||
67838c2ecf20Sopenharmony_ci	    netif_is_lag_port(port_dev) ||
67848c2ecf20Sopenharmony_ci	    netif_is_ovs_port(port_dev))
67858c2ecf20Sopenharmony_ci		return 0;
67868c2ecf20Sopenharmony_ci
67878c2ecf20Sopenharmony_ci	return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event,
67888c2ecf20Sopenharmony_ci						 MLXSW_SP_DEFAULT_VID, extack);
67898c2ecf20Sopenharmony_ci}
67908c2ecf20Sopenharmony_ci
67918c2ecf20Sopenharmony_cistatic int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
67928c2ecf20Sopenharmony_ci					 struct net_device *lag_dev,
67938c2ecf20Sopenharmony_ci					 unsigned long event, u16 vid,
67948c2ecf20Sopenharmony_ci					 struct netlink_ext_ack *extack)
67958c2ecf20Sopenharmony_ci{
67968c2ecf20Sopenharmony_ci	struct net_device *port_dev;
67978c2ecf20Sopenharmony_ci	struct list_head *iter;
67988c2ecf20Sopenharmony_ci	int err;
67998c2ecf20Sopenharmony_ci
68008c2ecf20Sopenharmony_ci	netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
68018c2ecf20Sopenharmony_ci		if (mlxsw_sp_port_dev_check(port_dev)) {
68028c2ecf20Sopenharmony_ci			err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
68038c2ecf20Sopenharmony_ci								port_dev,
68048c2ecf20Sopenharmony_ci								event, vid,
68058c2ecf20Sopenharmony_ci								extack);
68068c2ecf20Sopenharmony_ci			if (err)
68078c2ecf20Sopenharmony_ci				return err;
68088c2ecf20Sopenharmony_ci		}
68098c2ecf20Sopenharmony_ci	}
68108c2ecf20Sopenharmony_ci
68118c2ecf20Sopenharmony_ci	return 0;
68128c2ecf20Sopenharmony_ci}
68138c2ecf20Sopenharmony_ci
68148c2ecf20Sopenharmony_cistatic int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
68158c2ecf20Sopenharmony_ci				       unsigned long event,
68168c2ecf20Sopenharmony_ci				       struct netlink_ext_ack *extack)
68178c2ecf20Sopenharmony_ci{
68188c2ecf20Sopenharmony_ci	if (netif_is_bridge_port(lag_dev))
68198c2ecf20Sopenharmony_ci		return 0;
68208c2ecf20Sopenharmony_ci
68218c2ecf20Sopenharmony_ci	return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event,
68228c2ecf20Sopenharmony_ci					     MLXSW_SP_DEFAULT_VID, extack);
68238c2ecf20Sopenharmony_ci}
68248c2ecf20Sopenharmony_ci
68258c2ecf20Sopenharmony_cistatic int mlxsw_sp_inetaddr_bridge_event(struct mlxsw_sp *mlxsw_sp,
68268c2ecf20Sopenharmony_ci					  struct net_device *l3_dev,
68278c2ecf20Sopenharmony_ci					  unsigned long event,
68288c2ecf20Sopenharmony_ci					  struct netlink_ext_ack *extack)
68298c2ecf20Sopenharmony_ci{
68308c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_params params = {
68318c2ecf20Sopenharmony_ci		.dev = l3_dev,
68328c2ecf20Sopenharmony_ci	};
68338c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
68348c2ecf20Sopenharmony_ci
68358c2ecf20Sopenharmony_ci	switch (event) {
68368c2ecf20Sopenharmony_ci	case NETDEV_UP:
68378c2ecf20Sopenharmony_ci		rif = mlxsw_sp_rif_create(mlxsw_sp, &params, extack);
68388c2ecf20Sopenharmony_ci		if (IS_ERR(rif))
68398c2ecf20Sopenharmony_ci			return PTR_ERR(rif);
68408c2ecf20Sopenharmony_ci		break;
68418c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
68428c2ecf20Sopenharmony_ci		rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
68438c2ecf20Sopenharmony_ci		mlxsw_sp_rif_destroy(rif);
68448c2ecf20Sopenharmony_ci		break;
68458c2ecf20Sopenharmony_ci	}
68468c2ecf20Sopenharmony_ci
68478c2ecf20Sopenharmony_ci	return 0;
68488c2ecf20Sopenharmony_ci}
68498c2ecf20Sopenharmony_ci
68508c2ecf20Sopenharmony_cistatic int mlxsw_sp_inetaddr_vlan_event(struct mlxsw_sp *mlxsw_sp,
68518c2ecf20Sopenharmony_ci					struct net_device *vlan_dev,
68528c2ecf20Sopenharmony_ci					unsigned long event,
68538c2ecf20Sopenharmony_ci					struct netlink_ext_ack *extack)
68548c2ecf20Sopenharmony_ci{
68558c2ecf20Sopenharmony_ci	struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
68568c2ecf20Sopenharmony_ci	u16 vid = vlan_dev_vlan_id(vlan_dev);
68578c2ecf20Sopenharmony_ci
68588c2ecf20Sopenharmony_ci	if (netif_is_bridge_port(vlan_dev))
68598c2ecf20Sopenharmony_ci		return 0;
68608c2ecf20Sopenharmony_ci
68618c2ecf20Sopenharmony_ci	if (mlxsw_sp_port_dev_check(real_dev))
68628c2ecf20Sopenharmony_ci		return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
68638c2ecf20Sopenharmony_ci							 event, vid, extack);
68648c2ecf20Sopenharmony_ci	else if (netif_is_lag_master(real_dev))
68658c2ecf20Sopenharmony_ci		return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
68668c2ecf20Sopenharmony_ci						     vid, extack);
68678c2ecf20Sopenharmony_ci	else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
68688c2ecf20Sopenharmony_ci		return mlxsw_sp_inetaddr_bridge_event(mlxsw_sp, vlan_dev, event,
68698c2ecf20Sopenharmony_ci						      extack);
68708c2ecf20Sopenharmony_ci
68718c2ecf20Sopenharmony_ci	return 0;
68728c2ecf20Sopenharmony_ci}
68738c2ecf20Sopenharmony_ci
68748c2ecf20Sopenharmony_cistatic bool mlxsw_sp_rif_macvlan_is_vrrp4(const u8 *mac)
68758c2ecf20Sopenharmony_ci{
68768c2ecf20Sopenharmony_ci	u8 vrrp4[ETH_ALEN] = { 0x00, 0x00, 0x5e, 0x00, 0x01, 0x00 };
68778c2ecf20Sopenharmony_ci	u8 mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
68788c2ecf20Sopenharmony_ci
68798c2ecf20Sopenharmony_ci	return ether_addr_equal_masked(mac, vrrp4, mask);
68808c2ecf20Sopenharmony_ci}
68818c2ecf20Sopenharmony_ci
68828c2ecf20Sopenharmony_cistatic bool mlxsw_sp_rif_macvlan_is_vrrp6(const u8 *mac)
68838c2ecf20Sopenharmony_ci{
68848c2ecf20Sopenharmony_ci	u8 vrrp6[ETH_ALEN] = { 0x00, 0x00, 0x5e, 0x00, 0x02, 0x00 };
68858c2ecf20Sopenharmony_ci	u8 mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
68868c2ecf20Sopenharmony_ci
68878c2ecf20Sopenharmony_ci	return ether_addr_equal_masked(mac, vrrp6, mask);
68888c2ecf20Sopenharmony_ci}
68898c2ecf20Sopenharmony_ci
68908c2ecf20Sopenharmony_cistatic int mlxsw_sp_rif_vrrp_op(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
68918c2ecf20Sopenharmony_ci				const u8 *mac, bool adding)
68928c2ecf20Sopenharmony_ci{
68938c2ecf20Sopenharmony_ci	char ritr_pl[MLXSW_REG_RITR_LEN];
68948c2ecf20Sopenharmony_ci	u8 vrrp_id = adding ? mac[5] : 0;
68958c2ecf20Sopenharmony_ci	int err;
68968c2ecf20Sopenharmony_ci
68978c2ecf20Sopenharmony_ci	if (!mlxsw_sp_rif_macvlan_is_vrrp4(mac) &&
68988c2ecf20Sopenharmony_ci	    !mlxsw_sp_rif_macvlan_is_vrrp6(mac))
68998c2ecf20Sopenharmony_ci		return 0;
69008c2ecf20Sopenharmony_ci
69018c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
69028c2ecf20Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
69038c2ecf20Sopenharmony_ci	if (err)
69048c2ecf20Sopenharmony_ci		return err;
69058c2ecf20Sopenharmony_ci
69068c2ecf20Sopenharmony_ci	if (mlxsw_sp_rif_macvlan_is_vrrp4(mac))
69078c2ecf20Sopenharmony_ci		mlxsw_reg_ritr_if_vrrp_id_ipv4_set(ritr_pl, vrrp_id);
69088c2ecf20Sopenharmony_ci	else
69098c2ecf20Sopenharmony_ci		mlxsw_reg_ritr_if_vrrp_id_ipv6_set(ritr_pl, vrrp_id);
69108c2ecf20Sopenharmony_ci
69118c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
69128c2ecf20Sopenharmony_ci}
69138c2ecf20Sopenharmony_ci
69148c2ecf20Sopenharmony_cistatic int mlxsw_sp_rif_macvlan_add(struct mlxsw_sp *mlxsw_sp,
69158c2ecf20Sopenharmony_ci				    const struct net_device *macvlan_dev,
69168c2ecf20Sopenharmony_ci				    struct netlink_ext_ack *extack)
69178c2ecf20Sopenharmony_ci{
69188c2ecf20Sopenharmony_ci	struct macvlan_dev *vlan = netdev_priv(macvlan_dev);
69198c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
69208c2ecf20Sopenharmony_ci	int err;
69218c2ecf20Sopenharmony_ci
69228c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, vlan->lowerdev);
69238c2ecf20Sopenharmony_ci	if (!rif) {
69248c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "macvlan is only supported on top of router interfaces");
69258c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
69268c2ecf20Sopenharmony_ci	}
69278c2ecf20Sopenharmony_ci
69288c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_fdb_op(mlxsw_sp, macvlan_dev->dev_addr,
69298c2ecf20Sopenharmony_ci				  mlxsw_sp_fid_index(rif->fid), true);
69308c2ecf20Sopenharmony_ci	if (err)
69318c2ecf20Sopenharmony_ci		return err;
69328c2ecf20Sopenharmony_ci
69338c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_vrrp_op(mlxsw_sp, rif->rif_index,
69348c2ecf20Sopenharmony_ci				   macvlan_dev->dev_addr, true);
69358c2ecf20Sopenharmony_ci	if (err)
69368c2ecf20Sopenharmony_ci		goto err_rif_vrrp_add;
69378c2ecf20Sopenharmony_ci
69388c2ecf20Sopenharmony_ci	/* Make sure the bridge driver does not have this MAC pointing at
69398c2ecf20Sopenharmony_ci	 * some other port.
69408c2ecf20Sopenharmony_ci	 */
69418c2ecf20Sopenharmony_ci	if (rif->ops->fdb_del)
69428c2ecf20Sopenharmony_ci		rif->ops->fdb_del(rif, macvlan_dev->dev_addr);
69438c2ecf20Sopenharmony_ci
69448c2ecf20Sopenharmony_ci	return 0;
69458c2ecf20Sopenharmony_ci
69468c2ecf20Sopenharmony_cierr_rif_vrrp_add:
69478c2ecf20Sopenharmony_ci	mlxsw_sp_rif_fdb_op(mlxsw_sp, macvlan_dev->dev_addr,
69488c2ecf20Sopenharmony_ci			    mlxsw_sp_fid_index(rif->fid), false);
69498c2ecf20Sopenharmony_ci	return err;
69508c2ecf20Sopenharmony_ci}
69518c2ecf20Sopenharmony_ci
69528c2ecf20Sopenharmony_cistatic void __mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp,
69538c2ecf20Sopenharmony_ci				       const struct net_device *macvlan_dev)
69548c2ecf20Sopenharmony_ci{
69558c2ecf20Sopenharmony_ci	struct macvlan_dev *vlan = netdev_priv(macvlan_dev);
69568c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
69578c2ecf20Sopenharmony_ci
69588c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, vlan->lowerdev);
69598c2ecf20Sopenharmony_ci	/* If we do not have a RIF, then we already took care of
69608c2ecf20Sopenharmony_ci	 * removing the macvlan's MAC during RIF deletion.
69618c2ecf20Sopenharmony_ci	 */
69628c2ecf20Sopenharmony_ci	if (!rif)
69638c2ecf20Sopenharmony_ci		return;
69648c2ecf20Sopenharmony_ci	mlxsw_sp_rif_vrrp_op(mlxsw_sp, rif->rif_index, macvlan_dev->dev_addr,
69658c2ecf20Sopenharmony_ci			     false);
69668c2ecf20Sopenharmony_ci	mlxsw_sp_rif_fdb_op(mlxsw_sp, macvlan_dev->dev_addr,
69678c2ecf20Sopenharmony_ci			    mlxsw_sp_fid_index(rif->fid), false);
69688c2ecf20Sopenharmony_ci}
69698c2ecf20Sopenharmony_ci
69708c2ecf20Sopenharmony_civoid mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp,
69718c2ecf20Sopenharmony_ci			      const struct net_device *macvlan_dev)
69728c2ecf20Sopenharmony_ci{
69738c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
69748c2ecf20Sopenharmony_ci	__mlxsw_sp_rif_macvlan_del(mlxsw_sp, macvlan_dev);
69758c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
69768c2ecf20Sopenharmony_ci}
69778c2ecf20Sopenharmony_ci
69788c2ecf20Sopenharmony_cistatic int mlxsw_sp_inetaddr_macvlan_event(struct mlxsw_sp *mlxsw_sp,
69798c2ecf20Sopenharmony_ci					   struct net_device *macvlan_dev,
69808c2ecf20Sopenharmony_ci					   unsigned long event,
69818c2ecf20Sopenharmony_ci					   struct netlink_ext_ack *extack)
69828c2ecf20Sopenharmony_ci{
69838c2ecf20Sopenharmony_ci	switch (event) {
69848c2ecf20Sopenharmony_ci	case NETDEV_UP:
69858c2ecf20Sopenharmony_ci		return mlxsw_sp_rif_macvlan_add(mlxsw_sp, macvlan_dev, extack);
69868c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
69878c2ecf20Sopenharmony_ci		__mlxsw_sp_rif_macvlan_del(mlxsw_sp, macvlan_dev);
69888c2ecf20Sopenharmony_ci		break;
69898c2ecf20Sopenharmony_ci	}
69908c2ecf20Sopenharmony_ci
69918c2ecf20Sopenharmony_ci	return 0;
69928c2ecf20Sopenharmony_ci}
69938c2ecf20Sopenharmony_ci
69948c2ecf20Sopenharmony_cistatic int mlxsw_sp_router_port_check_rif_addr(struct mlxsw_sp *mlxsw_sp,
69958c2ecf20Sopenharmony_ci					       struct net_device *dev,
69968c2ecf20Sopenharmony_ci					       const unsigned char *dev_addr,
69978c2ecf20Sopenharmony_ci					       struct netlink_ext_ack *extack)
69988c2ecf20Sopenharmony_ci{
69998c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
70008c2ecf20Sopenharmony_ci	int i;
70018c2ecf20Sopenharmony_ci
70028c2ecf20Sopenharmony_ci	/* A RIF is not created for macvlan netdevs. Their MAC is used to
70038c2ecf20Sopenharmony_ci	 * populate the FDB
70048c2ecf20Sopenharmony_ci	 */
70058c2ecf20Sopenharmony_ci	if (netif_is_macvlan(dev) || netif_is_l3_master(dev))
70068c2ecf20Sopenharmony_ci		return 0;
70078c2ecf20Sopenharmony_ci
70088c2ecf20Sopenharmony_ci	for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
70098c2ecf20Sopenharmony_ci		rif = mlxsw_sp->router->rifs[i];
70108c2ecf20Sopenharmony_ci		if (rif && rif->ops &&
70118c2ecf20Sopenharmony_ci		    rif->ops->type == MLXSW_SP_RIF_TYPE_IPIP_LB)
70128c2ecf20Sopenharmony_ci			continue;
70138c2ecf20Sopenharmony_ci		if (rif && rif->dev && rif->dev != dev &&
70148c2ecf20Sopenharmony_ci		    !ether_addr_equal_masked(rif->dev->dev_addr, dev_addr,
70158c2ecf20Sopenharmony_ci					     mlxsw_sp->mac_mask)) {
70168c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "All router interface MAC addresses must have the same prefix");
70178c2ecf20Sopenharmony_ci			return -EINVAL;
70188c2ecf20Sopenharmony_ci		}
70198c2ecf20Sopenharmony_ci	}
70208c2ecf20Sopenharmony_ci
70218c2ecf20Sopenharmony_ci	return 0;
70228c2ecf20Sopenharmony_ci}
70238c2ecf20Sopenharmony_ci
70248c2ecf20Sopenharmony_cistatic int __mlxsw_sp_inetaddr_event(struct mlxsw_sp *mlxsw_sp,
70258c2ecf20Sopenharmony_ci				     struct net_device *dev,
70268c2ecf20Sopenharmony_ci				     unsigned long event,
70278c2ecf20Sopenharmony_ci				     struct netlink_ext_ack *extack)
70288c2ecf20Sopenharmony_ci{
70298c2ecf20Sopenharmony_ci	if (mlxsw_sp_port_dev_check(dev))
70308c2ecf20Sopenharmony_ci		return mlxsw_sp_inetaddr_port_event(dev, event, extack);
70318c2ecf20Sopenharmony_ci	else if (netif_is_lag_master(dev))
70328c2ecf20Sopenharmony_ci		return mlxsw_sp_inetaddr_lag_event(dev, event, extack);
70338c2ecf20Sopenharmony_ci	else if (netif_is_bridge_master(dev))
70348c2ecf20Sopenharmony_ci		return mlxsw_sp_inetaddr_bridge_event(mlxsw_sp, dev, event,
70358c2ecf20Sopenharmony_ci						      extack);
70368c2ecf20Sopenharmony_ci	else if (is_vlan_dev(dev))
70378c2ecf20Sopenharmony_ci		return mlxsw_sp_inetaddr_vlan_event(mlxsw_sp, dev, event,
70388c2ecf20Sopenharmony_ci						    extack);
70398c2ecf20Sopenharmony_ci	else if (netif_is_macvlan(dev))
70408c2ecf20Sopenharmony_ci		return mlxsw_sp_inetaddr_macvlan_event(mlxsw_sp, dev, event,
70418c2ecf20Sopenharmony_ci						       extack);
70428c2ecf20Sopenharmony_ci	else
70438c2ecf20Sopenharmony_ci		return 0;
70448c2ecf20Sopenharmony_ci}
70458c2ecf20Sopenharmony_ci
70468c2ecf20Sopenharmony_cistatic int mlxsw_sp_inetaddr_event(struct notifier_block *nb,
70478c2ecf20Sopenharmony_ci				   unsigned long event, void *ptr)
70488c2ecf20Sopenharmony_ci{
70498c2ecf20Sopenharmony_ci	struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
70508c2ecf20Sopenharmony_ci	struct net_device *dev = ifa->ifa_dev->dev;
70518c2ecf20Sopenharmony_ci	struct mlxsw_sp_router *router;
70528c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
70538c2ecf20Sopenharmony_ci	int err = 0;
70548c2ecf20Sopenharmony_ci
70558c2ecf20Sopenharmony_ci	/* NETDEV_UP event is handled by mlxsw_sp_inetaddr_valid_event */
70568c2ecf20Sopenharmony_ci	if (event == NETDEV_UP)
70578c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
70588c2ecf20Sopenharmony_ci
70598c2ecf20Sopenharmony_ci	router = container_of(nb, struct mlxsw_sp_router, inetaddr_nb);
70608c2ecf20Sopenharmony_ci	mutex_lock(&router->lock);
70618c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(router->mlxsw_sp, dev);
70628c2ecf20Sopenharmony_ci	if (!mlxsw_sp_rif_should_config(rif, dev, event))
70638c2ecf20Sopenharmony_ci		goto out;
70648c2ecf20Sopenharmony_ci
70658c2ecf20Sopenharmony_ci	err = __mlxsw_sp_inetaddr_event(router->mlxsw_sp, dev, event, NULL);
70668c2ecf20Sopenharmony_ciout:
70678c2ecf20Sopenharmony_ci	mutex_unlock(&router->lock);
70688c2ecf20Sopenharmony_ci	return notifier_from_errno(err);
70698c2ecf20Sopenharmony_ci}
70708c2ecf20Sopenharmony_ci
70718c2ecf20Sopenharmony_ciint mlxsw_sp_inetaddr_valid_event(struct notifier_block *unused,
70728c2ecf20Sopenharmony_ci				  unsigned long event, void *ptr)
70738c2ecf20Sopenharmony_ci{
70748c2ecf20Sopenharmony_ci	struct in_validator_info *ivi = (struct in_validator_info *) ptr;
70758c2ecf20Sopenharmony_ci	struct net_device *dev = ivi->ivi_dev->dev;
70768c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
70778c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
70788c2ecf20Sopenharmony_ci	int err = 0;
70798c2ecf20Sopenharmony_ci
70808c2ecf20Sopenharmony_ci	mlxsw_sp = mlxsw_sp_lower_get(dev);
70818c2ecf20Sopenharmony_ci	if (!mlxsw_sp)
70828c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
70838c2ecf20Sopenharmony_ci
70848c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
70858c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
70868c2ecf20Sopenharmony_ci	if (!mlxsw_sp_rif_should_config(rif, dev, event))
70878c2ecf20Sopenharmony_ci		goto out;
70888c2ecf20Sopenharmony_ci
70898c2ecf20Sopenharmony_ci	err = mlxsw_sp_router_port_check_rif_addr(mlxsw_sp, dev, dev->dev_addr,
70908c2ecf20Sopenharmony_ci						  ivi->extack);
70918c2ecf20Sopenharmony_ci	if (err)
70928c2ecf20Sopenharmony_ci		goto out;
70938c2ecf20Sopenharmony_ci
70948c2ecf20Sopenharmony_ci	err = __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, ivi->extack);
70958c2ecf20Sopenharmony_ciout:
70968c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
70978c2ecf20Sopenharmony_ci	return notifier_from_errno(err);
70988c2ecf20Sopenharmony_ci}
70998c2ecf20Sopenharmony_ci
71008c2ecf20Sopenharmony_cistruct mlxsw_sp_inet6addr_event_work {
71018c2ecf20Sopenharmony_ci	struct work_struct work;
71028c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
71038c2ecf20Sopenharmony_ci	struct net_device *dev;
71048c2ecf20Sopenharmony_ci	unsigned long event;
71058c2ecf20Sopenharmony_ci};
71068c2ecf20Sopenharmony_ci
71078c2ecf20Sopenharmony_cistatic void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
71088c2ecf20Sopenharmony_ci{
71098c2ecf20Sopenharmony_ci	struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
71108c2ecf20Sopenharmony_ci		container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
71118c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = inet6addr_work->mlxsw_sp;
71128c2ecf20Sopenharmony_ci	struct net_device *dev = inet6addr_work->dev;
71138c2ecf20Sopenharmony_ci	unsigned long event = inet6addr_work->event;
71148c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
71158c2ecf20Sopenharmony_ci
71168c2ecf20Sopenharmony_ci	rtnl_lock();
71178c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
71188c2ecf20Sopenharmony_ci
71198c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
71208c2ecf20Sopenharmony_ci	if (!mlxsw_sp_rif_should_config(rif, dev, event))
71218c2ecf20Sopenharmony_ci		goto out;
71228c2ecf20Sopenharmony_ci
71238c2ecf20Sopenharmony_ci	__mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, NULL);
71248c2ecf20Sopenharmony_ciout:
71258c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
71268c2ecf20Sopenharmony_ci	rtnl_unlock();
71278c2ecf20Sopenharmony_ci	dev_put(dev);
71288c2ecf20Sopenharmony_ci	kfree(inet6addr_work);
71298c2ecf20Sopenharmony_ci}
71308c2ecf20Sopenharmony_ci
71318c2ecf20Sopenharmony_ci/* Called with rcu_read_lock() */
71328c2ecf20Sopenharmony_cistatic int mlxsw_sp_inet6addr_event(struct notifier_block *nb,
71338c2ecf20Sopenharmony_ci				    unsigned long event, void *ptr)
71348c2ecf20Sopenharmony_ci{
71358c2ecf20Sopenharmony_ci	struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
71368c2ecf20Sopenharmony_ci	struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
71378c2ecf20Sopenharmony_ci	struct net_device *dev = if6->idev->dev;
71388c2ecf20Sopenharmony_ci	struct mlxsw_sp_router *router;
71398c2ecf20Sopenharmony_ci
71408c2ecf20Sopenharmony_ci	/* NETDEV_UP event is handled by mlxsw_sp_inet6addr_valid_event */
71418c2ecf20Sopenharmony_ci	if (event == NETDEV_UP)
71428c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
71438c2ecf20Sopenharmony_ci
71448c2ecf20Sopenharmony_ci	inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
71458c2ecf20Sopenharmony_ci	if (!inet6addr_work)
71468c2ecf20Sopenharmony_ci		return NOTIFY_BAD;
71478c2ecf20Sopenharmony_ci
71488c2ecf20Sopenharmony_ci	router = container_of(nb, struct mlxsw_sp_router, inet6addr_nb);
71498c2ecf20Sopenharmony_ci	INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
71508c2ecf20Sopenharmony_ci	inet6addr_work->mlxsw_sp = router->mlxsw_sp;
71518c2ecf20Sopenharmony_ci	inet6addr_work->dev = dev;
71528c2ecf20Sopenharmony_ci	inet6addr_work->event = event;
71538c2ecf20Sopenharmony_ci	dev_hold(dev);
71548c2ecf20Sopenharmony_ci	mlxsw_core_schedule_work(&inet6addr_work->work);
71558c2ecf20Sopenharmony_ci
71568c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
71578c2ecf20Sopenharmony_ci}
71588c2ecf20Sopenharmony_ci
71598c2ecf20Sopenharmony_ciint mlxsw_sp_inet6addr_valid_event(struct notifier_block *unused,
71608c2ecf20Sopenharmony_ci				   unsigned long event, void *ptr)
71618c2ecf20Sopenharmony_ci{
71628c2ecf20Sopenharmony_ci	struct in6_validator_info *i6vi = (struct in6_validator_info *) ptr;
71638c2ecf20Sopenharmony_ci	struct net_device *dev = i6vi->i6vi_dev->dev;
71648c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
71658c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
71668c2ecf20Sopenharmony_ci	int err = 0;
71678c2ecf20Sopenharmony_ci
71688c2ecf20Sopenharmony_ci	mlxsw_sp = mlxsw_sp_lower_get(dev);
71698c2ecf20Sopenharmony_ci	if (!mlxsw_sp)
71708c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
71718c2ecf20Sopenharmony_ci
71728c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
71738c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
71748c2ecf20Sopenharmony_ci	if (!mlxsw_sp_rif_should_config(rif, dev, event))
71758c2ecf20Sopenharmony_ci		goto out;
71768c2ecf20Sopenharmony_ci
71778c2ecf20Sopenharmony_ci	err = mlxsw_sp_router_port_check_rif_addr(mlxsw_sp, dev, dev->dev_addr,
71788c2ecf20Sopenharmony_ci						  i6vi->extack);
71798c2ecf20Sopenharmony_ci	if (err)
71808c2ecf20Sopenharmony_ci		goto out;
71818c2ecf20Sopenharmony_ci
71828c2ecf20Sopenharmony_ci	err = __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, i6vi->extack);
71838c2ecf20Sopenharmony_ciout:
71848c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
71858c2ecf20Sopenharmony_ci	return notifier_from_errno(err);
71868c2ecf20Sopenharmony_ci}
71878c2ecf20Sopenharmony_ci
71888c2ecf20Sopenharmony_cistatic int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
71898c2ecf20Sopenharmony_ci			     const char *mac, int mtu)
71908c2ecf20Sopenharmony_ci{
71918c2ecf20Sopenharmony_ci	char ritr_pl[MLXSW_REG_RITR_LEN];
71928c2ecf20Sopenharmony_ci	int err;
71938c2ecf20Sopenharmony_ci
71948c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
71958c2ecf20Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
71968c2ecf20Sopenharmony_ci	if (err)
71978c2ecf20Sopenharmony_ci		return err;
71988c2ecf20Sopenharmony_ci
71998c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
72008c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
72018c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
72028c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
72038c2ecf20Sopenharmony_ci}
72048c2ecf20Sopenharmony_ci
72058c2ecf20Sopenharmony_cistatic int
72068c2ecf20Sopenharmony_cimlxsw_sp_router_port_change_event(struct mlxsw_sp *mlxsw_sp,
72078c2ecf20Sopenharmony_ci				  struct mlxsw_sp_rif *rif)
72088c2ecf20Sopenharmony_ci{
72098c2ecf20Sopenharmony_ci	struct net_device *dev = rif->dev;
72108c2ecf20Sopenharmony_ci	u16 fid_index;
72118c2ecf20Sopenharmony_ci	int err;
72128c2ecf20Sopenharmony_ci
72138c2ecf20Sopenharmony_ci	fid_index = mlxsw_sp_fid_index(rif->fid);
72148c2ecf20Sopenharmony_ci
72158c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
72168c2ecf20Sopenharmony_ci	if (err)
72178c2ecf20Sopenharmony_ci		return err;
72188c2ecf20Sopenharmony_ci
72198c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
72208c2ecf20Sopenharmony_ci				dev->mtu);
72218c2ecf20Sopenharmony_ci	if (err)
72228c2ecf20Sopenharmony_ci		goto err_rif_edit;
72238c2ecf20Sopenharmony_ci
72248c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
72258c2ecf20Sopenharmony_ci	if (err)
72268c2ecf20Sopenharmony_ci		goto err_rif_fdb_op;
72278c2ecf20Sopenharmony_ci
72288c2ecf20Sopenharmony_ci	if (rif->mtu != dev->mtu) {
72298c2ecf20Sopenharmony_ci		struct mlxsw_sp_vr *vr;
72308c2ecf20Sopenharmony_ci		int i;
72318c2ecf20Sopenharmony_ci
72328c2ecf20Sopenharmony_ci		/* The RIF is relevant only to its mr_table instance, as unlike
72338c2ecf20Sopenharmony_ci		 * unicast routing, in multicast routing a RIF cannot be shared
72348c2ecf20Sopenharmony_ci		 * between several multicast routing tables.
72358c2ecf20Sopenharmony_ci		 */
72368c2ecf20Sopenharmony_ci		vr = &mlxsw_sp->router->vrs[rif->vr_id];
72378c2ecf20Sopenharmony_ci		for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++)
72388c2ecf20Sopenharmony_ci			mlxsw_sp_mr_rif_mtu_update(vr->mr_table[i],
72398c2ecf20Sopenharmony_ci						   rif, dev->mtu);
72408c2ecf20Sopenharmony_ci	}
72418c2ecf20Sopenharmony_ci
72428c2ecf20Sopenharmony_ci	ether_addr_copy(rif->addr, dev->dev_addr);
72438c2ecf20Sopenharmony_ci	rif->mtu = dev->mtu;
72448c2ecf20Sopenharmony_ci
72458c2ecf20Sopenharmony_ci	netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
72468c2ecf20Sopenharmony_ci
72478c2ecf20Sopenharmony_ci	return 0;
72488c2ecf20Sopenharmony_ci
72498c2ecf20Sopenharmony_cierr_rif_fdb_op:
72508c2ecf20Sopenharmony_ci	mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
72518c2ecf20Sopenharmony_cierr_rif_edit:
72528c2ecf20Sopenharmony_ci	mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
72538c2ecf20Sopenharmony_ci	return err;
72548c2ecf20Sopenharmony_ci}
72558c2ecf20Sopenharmony_ci
72568c2ecf20Sopenharmony_cistatic int mlxsw_sp_router_port_pre_changeaddr_event(struct mlxsw_sp_rif *rif,
72578c2ecf20Sopenharmony_ci			    struct netdev_notifier_pre_changeaddr_info *info)
72588c2ecf20Sopenharmony_ci{
72598c2ecf20Sopenharmony_ci	struct netlink_ext_ack *extack;
72608c2ecf20Sopenharmony_ci
72618c2ecf20Sopenharmony_ci	extack = netdev_notifier_info_to_extack(&info->info);
72628c2ecf20Sopenharmony_ci	return mlxsw_sp_router_port_check_rif_addr(rif->mlxsw_sp, rif->dev,
72638c2ecf20Sopenharmony_ci						   info->dev_addr, extack);
72648c2ecf20Sopenharmony_ci}
72658c2ecf20Sopenharmony_ci
72668c2ecf20Sopenharmony_ciint mlxsw_sp_netdevice_router_port_event(struct net_device *dev,
72678c2ecf20Sopenharmony_ci					 unsigned long event, void *ptr)
72688c2ecf20Sopenharmony_ci{
72698c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
72708c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
72718c2ecf20Sopenharmony_ci	int err = 0;
72728c2ecf20Sopenharmony_ci
72738c2ecf20Sopenharmony_ci	mlxsw_sp = mlxsw_sp_lower_get(dev);
72748c2ecf20Sopenharmony_ci	if (!mlxsw_sp)
72758c2ecf20Sopenharmony_ci		return 0;
72768c2ecf20Sopenharmony_ci
72778c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
72788c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
72798c2ecf20Sopenharmony_ci	if (!rif)
72808c2ecf20Sopenharmony_ci		goto out;
72818c2ecf20Sopenharmony_ci
72828c2ecf20Sopenharmony_ci	switch (event) {
72838c2ecf20Sopenharmony_ci	case NETDEV_CHANGEMTU:
72848c2ecf20Sopenharmony_ci	case NETDEV_CHANGEADDR:
72858c2ecf20Sopenharmony_ci		err = mlxsw_sp_router_port_change_event(mlxsw_sp, rif);
72868c2ecf20Sopenharmony_ci		break;
72878c2ecf20Sopenharmony_ci	case NETDEV_PRE_CHANGEADDR:
72888c2ecf20Sopenharmony_ci		err = mlxsw_sp_router_port_pre_changeaddr_event(rif, ptr);
72898c2ecf20Sopenharmony_ci		break;
72908c2ecf20Sopenharmony_ci	}
72918c2ecf20Sopenharmony_ci
72928c2ecf20Sopenharmony_ciout:
72938c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
72948c2ecf20Sopenharmony_ci	return err;
72958c2ecf20Sopenharmony_ci}
72968c2ecf20Sopenharmony_ci
72978c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
72988c2ecf20Sopenharmony_ci				  struct net_device *l3_dev,
72998c2ecf20Sopenharmony_ci				  struct netlink_ext_ack *extack)
73008c2ecf20Sopenharmony_ci{
73018c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
73028c2ecf20Sopenharmony_ci
73038c2ecf20Sopenharmony_ci	/* If netdev is already associated with a RIF, then we need to
73048c2ecf20Sopenharmony_ci	 * destroy it and create a new one with the new virtual router ID.
73058c2ecf20Sopenharmony_ci	 */
73068c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
73078c2ecf20Sopenharmony_ci	if (rif)
73088c2ecf20Sopenharmony_ci		__mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_DOWN,
73098c2ecf20Sopenharmony_ci					  extack);
73108c2ecf20Sopenharmony_ci
73118c2ecf20Sopenharmony_ci	return __mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_UP, extack);
73128c2ecf20Sopenharmony_ci}
73138c2ecf20Sopenharmony_ci
73148c2ecf20Sopenharmony_cistatic void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
73158c2ecf20Sopenharmony_ci				    struct net_device *l3_dev)
73168c2ecf20Sopenharmony_ci{
73178c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif;
73188c2ecf20Sopenharmony_ci
73198c2ecf20Sopenharmony_ci	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
73208c2ecf20Sopenharmony_ci	if (!rif)
73218c2ecf20Sopenharmony_ci		return;
73228c2ecf20Sopenharmony_ci	__mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_DOWN, NULL);
73238c2ecf20Sopenharmony_ci}
73248c2ecf20Sopenharmony_ci
73258c2ecf20Sopenharmony_ciint mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
73268c2ecf20Sopenharmony_ci				 struct netdev_notifier_changeupper_info *info)
73278c2ecf20Sopenharmony_ci{
73288c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
73298c2ecf20Sopenharmony_ci	int err = 0;
73308c2ecf20Sopenharmony_ci
73318c2ecf20Sopenharmony_ci	/* We do not create a RIF for a macvlan, but only use it to
73328c2ecf20Sopenharmony_ci	 * direct more MAC addresses to the router.
73338c2ecf20Sopenharmony_ci	 */
73348c2ecf20Sopenharmony_ci	if (!mlxsw_sp || netif_is_macvlan(l3_dev))
73358c2ecf20Sopenharmony_ci		return 0;
73368c2ecf20Sopenharmony_ci
73378c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
73388c2ecf20Sopenharmony_ci	switch (event) {
73398c2ecf20Sopenharmony_ci	case NETDEV_PRECHANGEUPPER:
73408c2ecf20Sopenharmony_ci		break;
73418c2ecf20Sopenharmony_ci	case NETDEV_CHANGEUPPER:
73428c2ecf20Sopenharmony_ci		if (info->linking) {
73438c2ecf20Sopenharmony_ci			struct netlink_ext_ack *extack;
73448c2ecf20Sopenharmony_ci
73458c2ecf20Sopenharmony_ci			extack = netdev_notifier_info_to_extack(&info->info);
73468c2ecf20Sopenharmony_ci			err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev, extack);
73478c2ecf20Sopenharmony_ci		} else {
73488c2ecf20Sopenharmony_ci			mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
73498c2ecf20Sopenharmony_ci		}
73508c2ecf20Sopenharmony_ci		break;
73518c2ecf20Sopenharmony_ci	}
73528c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
73538c2ecf20Sopenharmony_ci
73548c2ecf20Sopenharmony_ci	return err;
73558c2ecf20Sopenharmony_ci}
73568c2ecf20Sopenharmony_ci
73578c2ecf20Sopenharmony_cistatic int __mlxsw_sp_rif_macvlan_flush(struct net_device *dev,
73588c2ecf20Sopenharmony_ci					struct netdev_nested_priv *priv)
73598c2ecf20Sopenharmony_ci{
73608c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *rif = (struct mlxsw_sp_rif *)priv->data;
73618c2ecf20Sopenharmony_ci
73628c2ecf20Sopenharmony_ci	if (!netif_is_macvlan(dev))
73638c2ecf20Sopenharmony_ci		return 0;
73648c2ecf20Sopenharmony_ci
73658c2ecf20Sopenharmony_ci	return mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, dev->dev_addr,
73668c2ecf20Sopenharmony_ci				   mlxsw_sp_fid_index(rif->fid), false);
73678c2ecf20Sopenharmony_ci}
73688c2ecf20Sopenharmony_ci
73698c2ecf20Sopenharmony_cistatic int mlxsw_sp_rif_macvlan_flush(struct mlxsw_sp_rif *rif)
73708c2ecf20Sopenharmony_ci{
73718c2ecf20Sopenharmony_ci	struct netdev_nested_priv priv = {
73728c2ecf20Sopenharmony_ci		.data = (void *)rif,
73738c2ecf20Sopenharmony_ci	};
73748c2ecf20Sopenharmony_ci
73758c2ecf20Sopenharmony_ci	if (!netif_is_macvlan_port(rif->dev))
73768c2ecf20Sopenharmony_ci		return 0;
73778c2ecf20Sopenharmony_ci
73788c2ecf20Sopenharmony_ci	netdev_warn(rif->dev, "Router interface is deleted. Upper macvlans will not work\n");
73798c2ecf20Sopenharmony_ci	return netdev_walk_all_upper_dev_rcu(rif->dev,
73808c2ecf20Sopenharmony_ci					     __mlxsw_sp_rif_macvlan_flush, &priv);
73818c2ecf20Sopenharmony_ci}
73828c2ecf20Sopenharmony_ci
73838c2ecf20Sopenharmony_cistatic void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
73848c2ecf20Sopenharmony_ci				       const struct mlxsw_sp_rif_params *params)
73858c2ecf20Sopenharmony_ci{
73868c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_subport *rif_subport;
73878c2ecf20Sopenharmony_ci
73888c2ecf20Sopenharmony_ci	rif_subport = mlxsw_sp_rif_subport_rif(rif);
73898c2ecf20Sopenharmony_ci	refcount_set(&rif_subport->ref_count, 1);
73908c2ecf20Sopenharmony_ci	rif_subport->vid = params->vid;
73918c2ecf20Sopenharmony_ci	rif_subport->lag = params->lag;
73928c2ecf20Sopenharmony_ci	if (params->lag)
73938c2ecf20Sopenharmony_ci		rif_subport->lag_id = params->lag_id;
73948c2ecf20Sopenharmony_ci	else
73958c2ecf20Sopenharmony_ci		rif_subport->system_port = params->system_port;
73968c2ecf20Sopenharmony_ci}
73978c2ecf20Sopenharmony_ci
73988c2ecf20Sopenharmony_cistatic int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
73998c2ecf20Sopenharmony_ci{
74008c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
74018c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_subport *rif_subport;
74028c2ecf20Sopenharmony_ci	char ritr_pl[MLXSW_REG_RITR_LEN];
74038c2ecf20Sopenharmony_ci
74048c2ecf20Sopenharmony_ci	rif_subport = mlxsw_sp_rif_subport_rif(rif);
74058c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
74068c2ecf20Sopenharmony_ci			    rif->rif_index, rif->vr_id, rif->dev->mtu);
74078c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
74088c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
74098c2ecf20Sopenharmony_ci				  rif_subport->lag ? rif_subport->lag_id :
74108c2ecf20Sopenharmony_ci						     rif_subport->system_port,
74118c2ecf20Sopenharmony_ci				  rif_subport->vid);
74128c2ecf20Sopenharmony_ci
74138c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
74148c2ecf20Sopenharmony_ci}
74158c2ecf20Sopenharmony_ci
74168c2ecf20Sopenharmony_cistatic int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
74178c2ecf20Sopenharmony_ci{
74188c2ecf20Sopenharmony_ci	int err;
74198c2ecf20Sopenharmony_ci
74208c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_subport_op(rif, true);
74218c2ecf20Sopenharmony_ci	if (err)
74228c2ecf20Sopenharmony_ci		return err;
74238c2ecf20Sopenharmony_ci
74248c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
74258c2ecf20Sopenharmony_ci				  mlxsw_sp_fid_index(rif->fid), true);
74268c2ecf20Sopenharmony_ci	if (err)
74278c2ecf20Sopenharmony_ci		goto err_rif_fdb_op;
74288c2ecf20Sopenharmony_ci
74298c2ecf20Sopenharmony_ci	mlxsw_sp_fid_rif_set(rif->fid, rif);
74308c2ecf20Sopenharmony_ci	return 0;
74318c2ecf20Sopenharmony_ci
74328c2ecf20Sopenharmony_cierr_rif_fdb_op:
74338c2ecf20Sopenharmony_ci	mlxsw_sp_rif_subport_op(rif, false);
74348c2ecf20Sopenharmony_ci	return err;
74358c2ecf20Sopenharmony_ci}
74368c2ecf20Sopenharmony_ci
74378c2ecf20Sopenharmony_cistatic void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
74388c2ecf20Sopenharmony_ci{
74398c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid = rif->fid;
74408c2ecf20Sopenharmony_ci
74418c2ecf20Sopenharmony_ci	mlxsw_sp_fid_rif_set(fid, NULL);
74428c2ecf20Sopenharmony_ci	mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
74438c2ecf20Sopenharmony_ci			    mlxsw_sp_fid_index(fid), false);
74448c2ecf20Sopenharmony_ci	mlxsw_sp_rif_macvlan_flush(rif);
74458c2ecf20Sopenharmony_ci	mlxsw_sp_rif_subport_op(rif, false);
74468c2ecf20Sopenharmony_ci}
74478c2ecf20Sopenharmony_ci
74488c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fid *
74498c2ecf20Sopenharmony_cimlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif,
74508c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
74518c2ecf20Sopenharmony_ci{
74528c2ecf20Sopenharmony_ci	return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
74538c2ecf20Sopenharmony_ci}
74548c2ecf20Sopenharmony_ci
74558c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
74568c2ecf20Sopenharmony_ci	.type			= MLXSW_SP_RIF_TYPE_SUBPORT,
74578c2ecf20Sopenharmony_ci	.rif_size		= sizeof(struct mlxsw_sp_rif_subport),
74588c2ecf20Sopenharmony_ci	.setup			= mlxsw_sp_rif_subport_setup,
74598c2ecf20Sopenharmony_ci	.configure		= mlxsw_sp_rif_subport_configure,
74608c2ecf20Sopenharmony_ci	.deconfigure		= mlxsw_sp_rif_subport_deconfigure,
74618c2ecf20Sopenharmony_ci	.fid_get		= mlxsw_sp_rif_subport_fid_get,
74628c2ecf20Sopenharmony_ci};
74638c2ecf20Sopenharmony_ci
74648c2ecf20Sopenharmony_cistatic int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
74658c2ecf20Sopenharmony_ci				    enum mlxsw_reg_ritr_if_type type,
74668c2ecf20Sopenharmony_ci				    u16 vid_fid, bool enable)
74678c2ecf20Sopenharmony_ci{
74688c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
74698c2ecf20Sopenharmony_ci	char ritr_pl[MLXSW_REG_RITR_LEN];
74708c2ecf20Sopenharmony_ci
74718c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
74728c2ecf20Sopenharmony_ci			    rif->dev->mtu);
74738c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
74748c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
74758c2ecf20Sopenharmony_ci
74768c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
74778c2ecf20Sopenharmony_ci}
74788c2ecf20Sopenharmony_ci
74798c2ecf20Sopenharmony_ciu8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
74808c2ecf20Sopenharmony_ci{
74818c2ecf20Sopenharmony_ci	return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
74828c2ecf20Sopenharmony_ci}
74838c2ecf20Sopenharmony_ci
74848c2ecf20Sopenharmony_cistatic int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
74858c2ecf20Sopenharmony_ci{
74868c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
74878c2ecf20Sopenharmony_ci	u16 fid_index = mlxsw_sp_fid_index(rif->fid);
74888c2ecf20Sopenharmony_ci	int err;
74898c2ecf20Sopenharmony_ci
74908c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
74918c2ecf20Sopenharmony_ci				       true);
74928c2ecf20Sopenharmony_ci	if (err)
74938c2ecf20Sopenharmony_ci		return err;
74948c2ecf20Sopenharmony_ci
74958c2ecf20Sopenharmony_ci	err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
74968c2ecf20Sopenharmony_ci				     mlxsw_sp_router_port(mlxsw_sp), true);
74978c2ecf20Sopenharmony_ci	if (err)
74988c2ecf20Sopenharmony_ci		goto err_fid_mc_flood_set;
74998c2ecf20Sopenharmony_ci
75008c2ecf20Sopenharmony_ci	err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
75018c2ecf20Sopenharmony_ci				     mlxsw_sp_router_port(mlxsw_sp), true);
75028c2ecf20Sopenharmony_ci	if (err)
75038c2ecf20Sopenharmony_ci		goto err_fid_bc_flood_set;
75048c2ecf20Sopenharmony_ci
75058c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
75068c2ecf20Sopenharmony_ci				  mlxsw_sp_fid_index(rif->fid), true);
75078c2ecf20Sopenharmony_ci	if (err)
75088c2ecf20Sopenharmony_ci		goto err_rif_fdb_op;
75098c2ecf20Sopenharmony_ci
75108c2ecf20Sopenharmony_ci	mlxsw_sp_fid_rif_set(rif->fid, rif);
75118c2ecf20Sopenharmony_ci	return 0;
75128c2ecf20Sopenharmony_ci
75138c2ecf20Sopenharmony_cierr_rif_fdb_op:
75148c2ecf20Sopenharmony_ci	mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
75158c2ecf20Sopenharmony_ci			       mlxsw_sp_router_port(mlxsw_sp), false);
75168c2ecf20Sopenharmony_cierr_fid_bc_flood_set:
75178c2ecf20Sopenharmony_ci	mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
75188c2ecf20Sopenharmony_ci			       mlxsw_sp_router_port(mlxsw_sp), false);
75198c2ecf20Sopenharmony_cierr_fid_mc_flood_set:
75208c2ecf20Sopenharmony_ci	mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
75218c2ecf20Sopenharmony_ci	return err;
75228c2ecf20Sopenharmony_ci}
75238c2ecf20Sopenharmony_ci
75248c2ecf20Sopenharmony_cistatic void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
75258c2ecf20Sopenharmony_ci{
75268c2ecf20Sopenharmony_ci	u16 fid_index = mlxsw_sp_fid_index(rif->fid);
75278c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
75288c2ecf20Sopenharmony_ci	struct mlxsw_sp_fid *fid = rif->fid;
75298c2ecf20Sopenharmony_ci
75308c2ecf20Sopenharmony_ci	mlxsw_sp_fid_rif_set(fid, NULL);
75318c2ecf20Sopenharmony_ci	mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
75328c2ecf20Sopenharmony_ci			    mlxsw_sp_fid_index(fid), false);
75338c2ecf20Sopenharmony_ci	mlxsw_sp_rif_macvlan_flush(rif);
75348c2ecf20Sopenharmony_ci	mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
75358c2ecf20Sopenharmony_ci			       mlxsw_sp_router_port(mlxsw_sp), false);
75368c2ecf20Sopenharmony_ci	mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
75378c2ecf20Sopenharmony_ci			       mlxsw_sp_router_port(mlxsw_sp), false);
75388c2ecf20Sopenharmony_ci	mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
75398c2ecf20Sopenharmony_ci}
75408c2ecf20Sopenharmony_ci
75418c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fid *
75428c2ecf20Sopenharmony_cimlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif,
75438c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
75448c2ecf20Sopenharmony_ci{
75458c2ecf20Sopenharmony_ci	return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
75468c2ecf20Sopenharmony_ci}
75478c2ecf20Sopenharmony_ci
75488c2ecf20Sopenharmony_cistatic void mlxsw_sp_rif_fid_fdb_del(struct mlxsw_sp_rif *rif, const char *mac)
75498c2ecf20Sopenharmony_ci{
75508c2ecf20Sopenharmony_ci	struct switchdev_notifier_fdb_info info;
75518c2ecf20Sopenharmony_ci	struct net_device *dev;
75528c2ecf20Sopenharmony_ci
75538c2ecf20Sopenharmony_ci	dev = br_fdb_find_port(rif->dev, mac, 0);
75548c2ecf20Sopenharmony_ci	if (!dev)
75558c2ecf20Sopenharmony_ci		return;
75568c2ecf20Sopenharmony_ci
75578c2ecf20Sopenharmony_ci	info.addr = mac;
75588c2ecf20Sopenharmony_ci	info.vid = 0;
75598c2ecf20Sopenharmony_ci	call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, dev, &info.info,
75608c2ecf20Sopenharmony_ci				 NULL);
75618c2ecf20Sopenharmony_ci}
75628c2ecf20Sopenharmony_ci
75638c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
75648c2ecf20Sopenharmony_ci	.type			= MLXSW_SP_RIF_TYPE_FID,
75658c2ecf20Sopenharmony_ci	.rif_size		= sizeof(struct mlxsw_sp_rif),
75668c2ecf20Sopenharmony_ci	.configure		= mlxsw_sp_rif_fid_configure,
75678c2ecf20Sopenharmony_ci	.deconfigure		= mlxsw_sp_rif_fid_deconfigure,
75688c2ecf20Sopenharmony_ci	.fid_get		= mlxsw_sp_rif_fid_fid_get,
75698c2ecf20Sopenharmony_ci	.fdb_del		= mlxsw_sp_rif_fid_fdb_del,
75708c2ecf20Sopenharmony_ci};
75718c2ecf20Sopenharmony_ci
75728c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fid *
75738c2ecf20Sopenharmony_cimlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif,
75748c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
75758c2ecf20Sopenharmony_ci{
75768c2ecf20Sopenharmony_ci	struct net_device *br_dev;
75778c2ecf20Sopenharmony_ci	u16 vid;
75788c2ecf20Sopenharmony_ci	int err;
75798c2ecf20Sopenharmony_ci
75808c2ecf20Sopenharmony_ci	if (is_vlan_dev(rif->dev)) {
75818c2ecf20Sopenharmony_ci		vid = vlan_dev_vlan_id(rif->dev);
75828c2ecf20Sopenharmony_ci		br_dev = vlan_dev_real_dev(rif->dev);
75838c2ecf20Sopenharmony_ci		if (WARN_ON(!netif_is_bridge_master(br_dev)))
75848c2ecf20Sopenharmony_ci			return ERR_PTR(-EINVAL);
75858c2ecf20Sopenharmony_ci	} else {
75868c2ecf20Sopenharmony_ci		err = br_vlan_get_pvid(rif->dev, &vid);
75878c2ecf20Sopenharmony_ci		if (err < 0 || !vid) {
75888c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Couldn't determine bridge PVID");
75898c2ecf20Sopenharmony_ci			return ERR_PTR(-EINVAL);
75908c2ecf20Sopenharmony_ci		}
75918c2ecf20Sopenharmony_ci	}
75928c2ecf20Sopenharmony_ci
75938c2ecf20Sopenharmony_ci	return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
75948c2ecf20Sopenharmony_ci}
75958c2ecf20Sopenharmony_ci
75968c2ecf20Sopenharmony_cistatic void mlxsw_sp_rif_vlan_fdb_del(struct mlxsw_sp_rif *rif, const char *mac)
75978c2ecf20Sopenharmony_ci{
75988c2ecf20Sopenharmony_ci	u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
75998c2ecf20Sopenharmony_ci	struct switchdev_notifier_fdb_info info;
76008c2ecf20Sopenharmony_ci	struct net_device *br_dev;
76018c2ecf20Sopenharmony_ci	struct net_device *dev;
76028c2ecf20Sopenharmony_ci
76038c2ecf20Sopenharmony_ci	br_dev = is_vlan_dev(rif->dev) ? vlan_dev_real_dev(rif->dev) : rif->dev;
76048c2ecf20Sopenharmony_ci	dev = br_fdb_find_port(br_dev, mac, vid);
76058c2ecf20Sopenharmony_ci	if (!dev)
76068c2ecf20Sopenharmony_ci		return;
76078c2ecf20Sopenharmony_ci
76088c2ecf20Sopenharmony_ci	info.addr = mac;
76098c2ecf20Sopenharmony_ci	info.vid = vid;
76108c2ecf20Sopenharmony_ci	call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, dev, &info.info,
76118c2ecf20Sopenharmony_ci				 NULL);
76128c2ecf20Sopenharmony_ci}
76138c2ecf20Sopenharmony_ci
76148c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_emu_ops = {
76158c2ecf20Sopenharmony_ci	.type			= MLXSW_SP_RIF_TYPE_VLAN,
76168c2ecf20Sopenharmony_ci	.rif_size		= sizeof(struct mlxsw_sp_rif),
76178c2ecf20Sopenharmony_ci	.configure		= mlxsw_sp_rif_fid_configure,
76188c2ecf20Sopenharmony_ci	.deconfigure		= mlxsw_sp_rif_fid_deconfigure,
76198c2ecf20Sopenharmony_ci	.fid_get		= mlxsw_sp_rif_vlan_fid_get,
76208c2ecf20Sopenharmony_ci	.fdb_del		= mlxsw_sp_rif_vlan_fdb_del,
76218c2ecf20Sopenharmony_ci};
76228c2ecf20Sopenharmony_ci
76238c2ecf20Sopenharmony_cistatic struct mlxsw_sp_rif_ipip_lb *
76248c2ecf20Sopenharmony_cimlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif)
76258c2ecf20Sopenharmony_ci{
76268c2ecf20Sopenharmony_ci	return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
76278c2ecf20Sopenharmony_ci}
76288c2ecf20Sopenharmony_ci
76298c2ecf20Sopenharmony_cistatic void
76308c2ecf20Sopenharmony_cimlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif,
76318c2ecf20Sopenharmony_ci			   const struct mlxsw_sp_rif_params *params)
76328c2ecf20Sopenharmony_ci{
76338c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_params_ipip_lb *params_lb;
76348c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_ipip_lb *rif_lb;
76358c2ecf20Sopenharmony_ci
76368c2ecf20Sopenharmony_ci	params_lb = container_of(params, struct mlxsw_sp_rif_params_ipip_lb,
76378c2ecf20Sopenharmony_ci				 common);
76388c2ecf20Sopenharmony_ci	rif_lb = mlxsw_sp_rif_ipip_lb_rif(rif);
76398c2ecf20Sopenharmony_ci	rif_lb->lb_config = params_lb->lb_config;
76408c2ecf20Sopenharmony_ci}
76418c2ecf20Sopenharmony_ci
76428c2ecf20Sopenharmony_cistatic int
76438c2ecf20Sopenharmony_cimlxsw_sp1_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif)
76448c2ecf20Sopenharmony_ci{
76458c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
76468c2ecf20Sopenharmony_ci	u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev);
76478c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
76488c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *ul_vr;
76498c2ecf20Sopenharmony_ci	int err;
76508c2ecf20Sopenharmony_ci
76518c2ecf20Sopenharmony_ci	ul_vr = mlxsw_sp_vr_get(mlxsw_sp, ul_tb_id, NULL);
76528c2ecf20Sopenharmony_ci	if (IS_ERR(ul_vr))
76538c2ecf20Sopenharmony_ci		return PTR_ERR(ul_vr);
76548c2ecf20Sopenharmony_ci
76558c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr->id, 0, true);
76568c2ecf20Sopenharmony_ci	if (err)
76578c2ecf20Sopenharmony_ci		goto err_loopback_op;
76588c2ecf20Sopenharmony_ci
76598c2ecf20Sopenharmony_ci	lb_rif->ul_vr_id = ul_vr->id;
76608c2ecf20Sopenharmony_ci	lb_rif->ul_rif_id = 0;
76618c2ecf20Sopenharmony_ci	++ul_vr->rif_count;
76628c2ecf20Sopenharmony_ci	return 0;
76638c2ecf20Sopenharmony_ci
76648c2ecf20Sopenharmony_cierr_loopback_op:
76658c2ecf20Sopenharmony_ci	mlxsw_sp_vr_put(mlxsw_sp, ul_vr);
76668c2ecf20Sopenharmony_ci	return err;
76678c2ecf20Sopenharmony_ci}
76688c2ecf20Sopenharmony_ci
76698c2ecf20Sopenharmony_cistatic void mlxsw_sp1_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif)
76708c2ecf20Sopenharmony_ci{
76718c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
76728c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
76738c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *ul_vr;
76748c2ecf20Sopenharmony_ci
76758c2ecf20Sopenharmony_ci	ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id];
76768c2ecf20Sopenharmony_ci	mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr->id, 0, false);
76778c2ecf20Sopenharmony_ci
76788c2ecf20Sopenharmony_ci	--ul_vr->rif_count;
76798c2ecf20Sopenharmony_ci	mlxsw_sp_vr_put(mlxsw_sp, ul_vr);
76808c2ecf20Sopenharmony_ci}
76818c2ecf20Sopenharmony_ci
76828c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_rif_ops mlxsw_sp1_rif_ipip_lb_ops = {
76838c2ecf20Sopenharmony_ci	.type			= MLXSW_SP_RIF_TYPE_IPIP_LB,
76848c2ecf20Sopenharmony_ci	.rif_size		= sizeof(struct mlxsw_sp_rif_ipip_lb),
76858c2ecf20Sopenharmony_ci	.setup                  = mlxsw_sp_rif_ipip_lb_setup,
76868c2ecf20Sopenharmony_ci	.configure		= mlxsw_sp1_rif_ipip_lb_configure,
76878c2ecf20Sopenharmony_ci	.deconfigure		= mlxsw_sp1_rif_ipip_lb_deconfigure,
76888c2ecf20Sopenharmony_ci};
76898c2ecf20Sopenharmony_ci
76908c2ecf20Sopenharmony_ciconst struct mlxsw_sp_rif_ops *mlxsw_sp1_rif_ops_arr[] = {
76918c2ecf20Sopenharmony_ci	[MLXSW_SP_RIF_TYPE_SUBPORT]	= &mlxsw_sp_rif_subport_ops,
76928c2ecf20Sopenharmony_ci	[MLXSW_SP_RIF_TYPE_VLAN]	= &mlxsw_sp_rif_vlan_emu_ops,
76938c2ecf20Sopenharmony_ci	[MLXSW_SP_RIF_TYPE_FID]		= &mlxsw_sp_rif_fid_ops,
76948c2ecf20Sopenharmony_ci	[MLXSW_SP_RIF_TYPE_IPIP_LB]	= &mlxsw_sp1_rif_ipip_lb_ops,
76958c2ecf20Sopenharmony_ci};
76968c2ecf20Sopenharmony_ci
76978c2ecf20Sopenharmony_cistatic int
76988c2ecf20Sopenharmony_cimlxsw_sp_rif_ipip_lb_ul_rif_op(struct mlxsw_sp_rif *ul_rif, bool enable)
76998c2ecf20Sopenharmony_ci{
77008c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = ul_rif->mlxsw_sp;
77018c2ecf20Sopenharmony_ci	char ritr_pl[MLXSW_REG_RITR_LEN];
77028c2ecf20Sopenharmony_ci
77038c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,
77048c2ecf20Sopenharmony_ci			    ul_rif->rif_index, ul_rif->vr_id, IP_MAX_MTU);
77058c2ecf20Sopenharmony_ci	mlxsw_reg_ritr_loopback_protocol_set(ritr_pl,
77068c2ecf20Sopenharmony_ci					     MLXSW_REG_RITR_LOOPBACK_GENERIC);
77078c2ecf20Sopenharmony_ci
77088c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
77098c2ecf20Sopenharmony_ci}
77108c2ecf20Sopenharmony_ci
77118c2ecf20Sopenharmony_cistatic struct mlxsw_sp_rif *
77128c2ecf20Sopenharmony_cimlxsw_sp_ul_rif_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr,
77138c2ecf20Sopenharmony_ci		       struct netlink_ext_ack *extack)
77148c2ecf20Sopenharmony_ci{
77158c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *ul_rif;
77168c2ecf20Sopenharmony_ci	u16 rif_index;
77178c2ecf20Sopenharmony_ci	int err;
77188c2ecf20Sopenharmony_ci
77198c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
77208c2ecf20Sopenharmony_ci	if (err) {
77218c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported router interfaces");
77228c2ecf20Sopenharmony_ci		return ERR_PTR(err);
77238c2ecf20Sopenharmony_ci	}
77248c2ecf20Sopenharmony_ci
77258c2ecf20Sopenharmony_ci	ul_rif = mlxsw_sp_rif_alloc(sizeof(*ul_rif), rif_index, vr->id, NULL);
77268c2ecf20Sopenharmony_ci	if (!ul_rif)
77278c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
77288c2ecf20Sopenharmony_ci
77298c2ecf20Sopenharmony_ci	mlxsw_sp->router->rifs[rif_index] = ul_rif;
77308c2ecf20Sopenharmony_ci	ul_rif->mlxsw_sp = mlxsw_sp;
77318c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_ipip_lb_ul_rif_op(ul_rif, true);
77328c2ecf20Sopenharmony_ci	if (err)
77338c2ecf20Sopenharmony_ci		goto ul_rif_op_err;
77348c2ecf20Sopenharmony_ci
77358c2ecf20Sopenharmony_ci	return ul_rif;
77368c2ecf20Sopenharmony_ci
77378c2ecf20Sopenharmony_ciul_rif_op_err:
77388c2ecf20Sopenharmony_ci	mlxsw_sp->router->rifs[rif_index] = NULL;
77398c2ecf20Sopenharmony_ci	kfree(ul_rif);
77408c2ecf20Sopenharmony_ci	return ERR_PTR(err);
77418c2ecf20Sopenharmony_ci}
77428c2ecf20Sopenharmony_ci
77438c2ecf20Sopenharmony_cistatic void mlxsw_sp_ul_rif_destroy(struct mlxsw_sp_rif *ul_rif)
77448c2ecf20Sopenharmony_ci{
77458c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = ul_rif->mlxsw_sp;
77468c2ecf20Sopenharmony_ci
77478c2ecf20Sopenharmony_ci	mlxsw_sp_rif_ipip_lb_ul_rif_op(ul_rif, false);
77488c2ecf20Sopenharmony_ci	mlxsw_sp->router->rifs[ul_rif->rif_index] = NULL;
77498c2ecf20Sopenharmony_ci	kfree(ul_rif);
77508c2ecf20Sopenharmony_ci}
77518c2ecf20Sopenharmony_ci
77528c2ecf20Sopenharmony_cistatic struct mlxsw_sp_rif *
77538c2ecf20Sopenharmony_cimlxsw_sp_ul_rif_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id,
77548c2ecf20Sopenharmony_ci		    struct netlink_ext_ack *extack)
77558c2ecf20Sopenharmony_ci{
77568c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
77578c2ecf20Sopenharmony_ci	int err;
77588c2ecf20Sopenharmony_ci
77598c2ecf20Sopenharmony_ci	vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id, extack);
77608c2ecf20Sopenharmony_ci	if (IS_ERR(vr))
77618c2ecf20Sopenharmony_ci		return ERR_CAST(vr);
77628c2ecf20Sopenharmony_ci
77638c2ecf20Sopenharmony_ci	if (refcount_inc_not_zero(&vr->ul_rif_refcnt))
77648c2ecf20Sopenharmony_ci		return vr->ul_rif;
77658c2ecf20Sopenharmony_ci
77668c2ecf20Sopenharmony_ci	vr->ul_rif = mlxsw_sp_ul_rif_create(mlxsw_sp, vr, extack);
77678c2ecf20Sopenharmony_ci	if (IS_ERR(vr->ul_rif)) {
77688c2ecf20Sopenharmony_ci		err = PTR_ERR(vr->ul_rif);
77698c2ecf20Sopenharmony_ci		goto err_ul_rif_create;
77708c2ecf20Sopenharmony_ci	}
77718c2ecf20Sopenharmony_ci
77728c2ecf20Sopenharmony_ci	vr->rif_count++;
77738c2ecf20Sopenharmony_ci	refcount_set(&vr->ul_rif_refcnt, 1);
77748c2ecf20Sopenharmony_ci
77758c2ecf20Sopenharmony_ci	return vr->ul_rif;
77768c2ecf20Sopenharmony_ci
77778c2ecf20Sopenharmony_cierr_ul_rif_create:
77788c2ecf20Sopenharmony_ci	mlxsw_sp_vr_put(mlxsw_sp, vr);
77798c2ecf20Sopenharmony_ci	return ERR_PTR(err);
77808c2ecf20Sopenharmony_ci}
77818c2ecf20Sopenharmony_ci
77828c2ecf20Sopenharmony_cistatic void mlxsw_sp_ul_rif_put(struct mlxsw_sp_rif *ul_rif)
77838c2ecf20Sopenharmony_ci{
77848c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = ul_rif->mlxsw_sp;
77858c2ecf20Sopenharmony_ci	struct mlxsw_sp_vr *vr;
77868c2ecf20Sopenharmony_ci
77878c2ecf20Sopenharmony_ci	vr = &mlxsw_sp->router->vrs[ul_rif->vr_id];
77888c2ecf20Sopenharmony_ci
77898c2ecf20Sopenharmony_ci	if (!refcount_dec_and_test(&vr->ul_rif_refcnt))
77908c2ecf20Sopenharmony_ci		return;
77918c2ecf20Sopenharmony_ci
77928c2ecf20Sopenharmony_ci	vr->rif_count--;
77938c2ecf20Sopenharmony_ci	mlxsw_sp_ul_rif_destroy(ul_rif);
77948c2ecf20Sopenharmony_ci	mlxsw_sp_vr_put(mlxsw_sp, vr);
77958c2ecf20Sopenharmony_ci}
77968c2ecf20Sopenharmony_ci
77978c2ecf20Sopenharmony_ciint mlxsw_sp_router_ul_rif_get(struct mlxsw_sp *mlxsw_sp, u32 ul_tb_id,
77988c2ecf20Sopenharmony_ci			       u16 *ul_rif_index)
77998c2ecf20Sopenharmony_ci{
78008c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *ul_rif;
78018c2ecf20Sopenharmony_ci	int err = 0;
78028c2ecf20Sopenharmony_ci
78038c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
78048c2ecf20Sopenharmony_ci	ul_rif = mlxsw_sp_ul_rif_get(mlxsw_sp, ul_tb_id, NULL);
78058c2ecf20Sopenharmony_ci	if (IS_ERR(ul_rif)) {
78068c2ecf20Sopenharmony_ci		err = PTR_ERR(ul_rif);
78078c2ecf20Sopenharmony_ci		goto out;
78088c2ecf20Sopenharmony_ci	}
78098c2ecf20Sopenharmony_ci	*ul_rif_index = ul_rif->rif_index;
78108c2ecf20Sopenharmony_ciout:
78118c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
78128c2ecf20Sopenharmony_ci	return err;
78138c2ecf20Sopenharmony_ci}
78148c2ecf20Sopenharmony_ci
78158c2ecf20Sopenharmony_civoid mlxsw_sp_router_ul_rif_put(struct mlxsw_sp *mlxsw_sp, u16 ul_rif_index)
78168c2ecf20Sopenharmony_ci{
78178c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *ul_rif;
78188c2ecf20Sopenharmony_ci
78198c2ecf20Sopenharmony_ci	mutex_lock(&mlxsw_sp->router->lock);
78208c2ecf20Sopenharmony_ci	ul_rif = mlxsw_sp->router->rifs[ul_rif_index];
78218c2ecf20Sopenharmony_ci	if (WARN_ON(!ul_rif))
78228c2ecf20Sopenharmony_ci		goto out;
78238c2ecf20Sopenharmony_ci
78248c2ecf20Sopenharmony_ci	mlxsw_sp_ul_rif_put(ul_rif);
78258c2ecf20Sopenharmony_ciout:
78268c2ecf20Sopenharmony_ci	mutex_unlock(&mlxsw_sp->router->lock);
78278c2ecf20Sopenharmony_ci}
78288c2ecf20Sopenharmony_ci
78298c2ecf20Sopenharmony_cistatic int
78308c2ecf20Sopenharmony_cimlxsw_sp2_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif)
78318c2ecf20Sopenharmony_ci{
78328c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
78338c2ecf20Sopenharmony_ci	u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev);
78348c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
78358c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *ul_rif;
78368c2ecf20Sopenharmony_ci	int err;
78378c2ecf20Sopenharmony_ci
78388c2ecf20Sopenharmony_ci	ul_rif = mlxsw_sp_ul_rif_get(mlxsw_sp, ul_tb_id, NULL);
78398c2ecf20Sopenharmony_ci	if (IS_ERR(ul_rif))
78408c2ecf20Sopenharmony_ci		return PTR_ERR(ul_rif);
78418c2ecf20Sopenharmony_ci
78428c2ecf20Sopenharmony_ci	err = mlxsw_sp_rif_ipip_lb_op(lb_rif, 0, ul_rif->rif_index, true);
78438c2ecf20Sopenharmony_ci	if (err)
78448c2ecf20Sopenharmony_ci		goto err_loopback_op;
78458c2ecf20Sopenharmony_ci
78468c2ecf20Sopenharmony_ci	lb_rif->ul_vr_id = 0;
78478c2ecf20Sopenharmony_ci	lb_rif->ul_rif_id = ul_rif->rif_index;
78488c2ecf20Sopenharmony_ci
78498c2ecf20Sopenharmony_ci	return 0;
78508c2ecf20Sopenharmony_ci
78518c2ecf20Sopenharmony_cierr_loopback_op:
78528c2ecf20Sopenharmony_ci	mlxsw_sp_ul_rif_put(ul_rif);
78538c2ecf20Sopenharmony_ci	return err;
78548c2ecf20Sopenharmony_ci}
78558c2ecf20Sopenharmony_ci
78568c2ecf20Sopenharmony_cistatic void mlxsw_sp2_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif)
78578c2ecf20Sopenharmony_ci{
78588c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
78598c2ecf20Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
78608c2ecf20Sopenharmony_ci	struct mlxsw_sp_rif *ul_rif;
78618c2ecf20Sopenharmony_ci
78628c2ecf20Sopenharmony_ci	ul_rif = mlxsw_sp_rif_by_index(mlxsw_sp, lb_rif->ul_rif_id);
78638c2ecf20Sopenharmony_ci	mlxsw_sp_rif_ipip_lb_op(lb_rif, 0, lb_rif->ul_rif_id, false);
78648c2ecf20Sopenharmony_ci	mlxsw_sp_ul_rif_put(ul_rif);
78658c2ecf20Sopenharmony_ci}
78668c2ecf20Sopenharmony_ci
78678c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_rif_ops mlxsw_sp2_rif_ipip_lb_ops = {
78688c2ecf20Sopenharmony_ci	.type			= MLXSW_SP_RIF_TYPE_IPIP_LB,
78698c2ecf20Sopenharmony_ci	.rif_size		= sizeof(struct mlxsw_sp_rif_ipip_lb),
78708c2ecf20Sopenharmony_ci	.setup                  = mlxsw_sp_rif_ipip_lb_setup,
78718c2ecf20Sopenharmony_ci	.configure		= mlxsw_sp2_rif_ipip_lb_configure,
78728c2ecf20Sopenharmony_ci	.deconfigure		= mlxsw_sp2_rif_ipip_lb_deconfigure,
78738c2ecf20Sopenharmony_ci};
78748c2ecf20Sopenharmony_ci
78758c2ecf20Sopenharmony_ciconst struct mlxsw_sp_rif_ops *mlxsw_sp2_rif_ops_arr[] = {
78768c2ecf20Sopenharmony_ci	[MLXSW_SP_RIF_TYPE_SUBPORT]	= &mlxsw_sp_rif_subport_ops,
78778c2ecf20Sopenharmony_ci	[MLXSW_SP_RIF_TYPE_VLAN]	= &mlxsw_sp_rif_vlan_emu_ops,
78788c2ecf20Sopenharmony_ci	[MLXSW_SP_RIF_TYPE_FID]		= &mlxsw_sp_rif_fid_ops,
78798c2ecf20Sopenharmony_ci	[MLXSW_SP_RIF_TYPE_IPIP_LB]	= &mlxsw_sp2_rif_ipip_lb_ops,
78808c2ecf20Sopenharmony_ci};
78818c2ecf20Sopenharmony_ci
78828c2ecf20Sopenharmony_cistatic int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
78838c2ecf20Sopenharmony_ci{
78848c2ecf20Sopenharmony_ci	u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
78858c2ecf20Sopenharmony_ci
78868c2ecf20Sopenharmony_ci	mlxsw_sp->router->rifs = kcalloc(max_rifs,
78878c2ecf20Sopenharmony_ci					 sizeof(struct mlxsw_sp_rif *),
78888c2ecf20Sopenharmony_ci					 GFP_KERNEL);
78898c2ecf20Sopenharmony_ci	if (!mlxsw_sp->router->rifs)
78908c2ecf20Sopenharmony_ci		return -ENOMEM;
78918c2ecf20Sopenharmony_ci
78928c2ecf20Sopenharmony_ci	return 0;
78938c2ecf20Sopenharmony_ci}
78948c2ecf20Sopenharmony_ci
78958c2ecf20Sopenharmony_cistatic void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
78968c2ecf20Sopenharmony_ci{
78978c2ecf20Sopenharmony_ci	int i;
78988c2ecf20Sopenharmony_ci
78998c2ecf20Sopenharmony_ci	for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
79008c2ecf20Sopenharmony_ci		WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
79018c2ecf20Sopenharmony_ci
79028c2ecf20Sopenharmony_ci	kfree(mlxsw_sp->router->rifs);
79038c2ecf20Sopenharmony_ci}
79048c2ecf20Sopenharmony_ci
79058c2ecf20Sopenharmony_cistatic int
79068c2ecf20Sopenharmony_cimlxsw_sp_ipip_config_tigcr(struct mlxsw_sp *mlxsw_sp)
79078c2ecf20Sopenharmony_ci{
79088c2ecf20Sopenharmony_ci	char tigcr_pl[MLXSW_REG_TIGCR_LEN];
79098c2ecf20Sopenharmony_ci
79108c2ecf20Sopenharmony_ci	mlxsw_reg_tigcr_pack(tigcr_pl, true, 0);
79118c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tigcr), tigcr_pl);
79128c2ecf20Sopenharmony_ci}
79138c2ecf20Sopenharmony_ci
79148c2ecf20Sopenharmony_cistatic int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp)
79158c2ecf20Sopenharmony_ci{
79168c2ecf20Sopenharmony_ci	int err;
79178c2ecf20Sopenharmony_ci
79188c2ecf20Sopenharmony_ci	mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr;
79198c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mlxsw_sp->router->ipip_list);
79208c2ecf20Sopenharmony_ci
79218c2ecf20Sopenharmony_ci	err = mlxsw_sp_ipip_ecn_encap_init(mlxsw_sp);
79228c2ecf20Sopenharmony_ci	if (err)
79238c2ecf20Sopenharmony_ci		return err;
79248c2ecf20Sopenharmony_ci	err = mlxsw_sp_ipip_ecn_decap_init(mlxsw_sp);
79258c2ecf20Sopenharmony_ci	if (err)
79268c2ecf20Sopenharmony_ci		return err;
79278c2ecf20Sopenharmony_ci
79288c2ecf20Sopenharmony_ci	return mlxsw_sp_ipip_config_tigcr(mlxsw_sp);
79298c2ecf20Sopenharmony_ci}
79308c2ecf20Sopenharmony_ci
79318c2ecf20Sopenharmony_cistatic void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp)
79328c2ecf20Sopenharmony_ci{
79338c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&mlxsw_sp->router->ipip_list));
79348c2ecf20Sopenharmony_ci}
79358c2ecf20Sopenharmony_ci
79368c2ecf20Sopenharmony_cistatic void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
79378c2ecf20Sopenharmony_ci{
79388c2ecf20Sopenharmony_ci	struct mlxsw_sp_router *router;
79398c2ecf20Sopenharmony_ci
79408c2ecf20Sopenharmony_ci	/* Flush pending FIB notifications and then flush the device's
79418c2ecf20Sopenharmony_ci	 * table before requesting another dump. The FIB notification
79428c2ecf20Sopenharmony_ci	 * block is unregistered, so no need to take RTNL.
79438c2ecf20Sopenharmony_ci	 */
79448c2ecf20Sopenharmony_ci	mlxsw_core_flush_owq();
79458c2ecf20Sopenharmony_ci	router = container_of(nb, struct mlxsw_sp_router, fib_nb);
79468c2ecf20Sopenharmony_ci	mlxsw_sp_router_fib_flush(router->mlxsw_sp);
79478c2ecf20Sopenharmony_ci}
79488c2ecf20Sopenharmony_ci
79498c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_MULTIPATH
79508c2ecf20Sopenharmony_cistatic void mlxsw_sp_mp_hash_header_set(char *recr2_pl, int header)
79518c2ecf20Sopenharmony_ci{
79528c2ecf20Sopenharmony_ci	mlxsw_reg_recr2_outer_header_enables_set(recr2_pl, header, true);
79538c2ecf20Sopenharmony_ci}
79548c2ecf20Sopenharmony_ci
79558c2ecf20Sopenharmony_cistatic void mlxsw_sp_mp_hash_field_set(char *recr2_pl, int field)
79568c2ecf20Sopenharmony_ci{
79578c2ecf20Sopenharmony_ci	mlxsw_reg_recr2_outer_header_fields_enable_set(recr2_pl, field, true);
79588c2ecf20Sopenharmony_ci}
79598c2ecf20Sopenharmony_ci
79608c2ecf20Sopenharmony_cistatic void mlxsw_sp_mp4_hash_init(struct mlxsw_sp *mlxsw_sp, char *recr2_pl)
79618c2ecf20Sopenharmony_ci{
79628c2ecf20Sopenharmony_ci	struct net *net = mlxsw_sp_net(mlxsw_sp);
79638c2ecf20Sopenharmony_ci	bool only_l3 = !net->ipv4.sysctl_fib_multipath_hash_policy;
79648c2ecf20Sopenharmony_ci
79658c2ecf20Sopenharmony_ci	mlxsw_sp_mp_hash_header_set(recr2_pl,
79668c2ecf20Sopenharmony_ci				    MLXSW_REG_RECR2_IPV4_EN_NOT_TCP_NOT_UDP);
79678c2ecf20Sopenharmony_ci	mlxsw_sp_mp_hash_header_set(recr2_pl, MLXSW_REG_RECR2_IPV4_EN_TCP_UDP);
79688c2ecf20Sopenharmony_ci	mlxsw_reg_recr2_ipv4_sip_enable(recr2_pl);
79698c2ecf20Sopenharmony_ci	mlxsw_reg_recr2_ipv4_dip_enable(recr2_pl);
79708c2ecf20Sopenharmony_ci	if (only_l3)
79718c2ecf20Sopenharmony_ci		return;
79728c2ecf20Sopenharmony_ci	mlxsw_sp_mp_hash_header_set(recr2_pl, MLXSW_REG_RECR2_TCP_UDP_EN_IPV4);
79738c2ecf20Sopenharmony_ci	mlxsw_sp_mp_hash_field_set(recr2_pl, MLXSW_REG_RECR2_IPV4_PROTOCOL);
79748c2ecf20Sopenharmony_ci	mlxsw_sp_mp_hash_field_set(recr2_pl, MLXSW_REG_RECR2_TCP_UDP_SPORT);
79758c2ecf20Sopenharmony_ci	mlxsw_sp_mp_hash_field_set(recr2_pl, MLXSW_REG_RECR2_TCP_UDP_DPORT);
79768c2ecf20Sopenharmony_ci}
79778c2ecf20Sopenharmony_ci
79788c2ecf20Sopenharmony_cistatic void mlxsw_sp_mp6_hash_init(struct mlxsw_sp *mlxsw_sp, char *recr2_pl)
79798c2ecf20Sopenharmony_ci{
79808c2ecf20Sopenharmony_ci	bool only_l3 = !ip6_multipath_hash_policy(mlxsw_sp_net(mlxsw_sp));
79818c2ecf20Sopenharmony_ci
79828c2ecf20Sopenharmony_ci	mlxsw_sp_mp_hash_header_set(recr2_pl,
79838c2ecf20Sopenharmony_ci				    MLXSW_REG_RECR2_IPV6_EN_NOT_TCP_NOT_UDP);
79848c2ecf20Sopenharmony_ci	mlxsw_sp_mp_hash_header_set(recr2_pl, MLXSW_REG_RECR2_IPV6_EN_TCP_UDP);
79858c2ecf20Sopenharmony_ci	mlxsw_reg_recr2_ipv6_sip_enable(recr2_pl);
79868c2ecf20Sopenharmony_ci	mlxsw_reg_recr2_ipv6_dip_enable(recr2_pl);
79878c2ecf20Sopenharmony_ci	mlxsw_sp_mp_hash_field_set(recr2_pl, MLXSW_REG_RECR2_IPV6_NEXT_HEADER);
79888c2ecf20Sopenharmony_ci	if (only_l3) {
79898c2ecf20Sopenharmony_ci		mlxsw_sp_mp_hash_field_set(recr2_pl,
79908c2ecf20Sopenharmony_ci					   MLXSW_REG_RECR2_IPV6_FLOW_LABEL);
79918c2ecf20Sopenharmony_ci	} else {
79928c2ecf20Sopenharmony_ci		mlxsw_sp_mp_hash_header_set(recr2_pl,
79938c2ecf20Sopenharmony_ci					    MLXSW_REG_RECR2_TCP_UDP_EN_IPV6);
79948c2ecf20Sopenharmony_ci		mlxsw_sp_mp_hash_field_set(recr2_pl,
79958c2ecf20Sopenharmony_ci					   MLXSW_REG_RECR2_TCP_UDP_SPORT);
79968c2ecf20Sopenharmony_ci		mlxsw_sp_mp_hash_field_set(recr2_pl,
79978c2ecf20Sopenharmony_ci					   MLXSW_REG_RECR2_TCP_UDP_DPORT);
79988c2ecf20Sopenharmony_ci	}
79998c2ecf20Sopenharmony_ci}
80008c2ecf20Sopenharmony_ci
80018c2ecf20Sopenharmony_cistatic int mlxsw_sp_mp_hash_init(struct mlxsw_sp *mlxsw_sp)
80028c2ecf20Sopenharmony_ci{
80038c2ecf20Sopenharmony_ci	char recr2_pl[MLXSW_REG_RECR2_LEN];
80048c2ecf20Sopenharmony_ci	u32 seed;
80058c2ecf20Sopenharmony_ci
80068c2ecf20Sopenharmony_ci	seed = jhash(mlxsw_sp->base_mac, sizeof(mlxsw_sp->base_mac), 0);
80078c2ecf20Sopenharmony_ci	mlxsw_reg_recr2_pack(recr2_pl, seed);
80088c2ecf20Sopenharmony_ci	mlxsw_sp_mp4_hash_init(mlxsw_sp, recr2_pl);
80098c2ecf20Sopenharmony_ci	mlxsw_sp_mp6_hash_init(mlxsw_sp, recr2_pl);
80108c2ecf20Sopenharmony_ci
80118c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(recr2), recr2_pl);
80128c2ecf20Sopenharmony_ci}
80138c2ecf20Sopenharmony_ci#else
80148c2ecf20Sopenharmony_cistatic int mlxsw_sp_mp_hash_init(struct mlxsw_sp *mlxsw_sp)
80158c2ecf20Sopenharmony_ci{
80168c2ecf20Sopenharmony_ci	return 0;
80178c2ecf20Sopenharmony_ci}
80188c2ecf20Sopenharmony_ci#endif
80198c2ecf20Sopenharmony_ci
80208c2ecf20Sopenharmony_cistatic int mlxsw_sp_dscp_init(struct mlxsw_sp *mlxsw_sp)
80218c2ecf20Sopenharmony_ci{
80228c2ecf20Sopenharmony_ci	char rdpm_pl[MLXSW_REG_RDPM_LEN];
80238c2ecf20Sopenharmony_ci	unsigned int i;
80248c2ecf20Sopenharmony_ci
80258c2ecf20Sopenharmony_ci	MLXSW_REG_ZERO(rdpm, rdpm_pl);
80268c2ecf20Sopenharmony_ci
80278c2ecf20Sopenharmony_ci	/* HW is determining switch priority based on DSCP-bits, but the
80288c2ecf20Sopenharmony_ci	 * kernel is still doing that based on the ToS. Since there's a
80298c2ecf20Sopenharmony_ci	 * mismatch in bits we need to make sure to translate the right
80308c2ecf20Sopenharmony_ci	 * value ToS would observe, skipping the 2 least-significant ECN bits.
80318c2ecf20Sopenharmony_ci	 */
80328c2ecf20Sopenharmony_ci	for (i = 0; i < MLXSW_REG_RDPM_DSCP_ENTRY_REC_MAX_COUNT; i++)
80338c2ecf20Sopenharmony_ci		mlxsw_reg_rdpm_pack(rdpm_pl, i, rt_tos2priority(i << 2));
80348c2ecf20Sopenharmony_ci
80358c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rdpm), rdpm_pl);
80368c2ecf20Sopenharmony_ci}
80378c2ecf20Sopenharmony_ci
80388c2ecf20Sopenharmony_cistatic int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
80398c2ecf20Sopenharmony_ci{
80408c2ecf20Sopenharmony_ci	struct net *net = mlxsw_sp_net(mlxsw_sp);
80418c2ecf20Sopenharmony_ci	char rgcr_pl[MLXSW_REG_RGCR_LEN];
80428c2ecf20Sopenharmony_ci	u64 max_rifs;
80438c2ecf20Sopenharmony_ci	bool usp;
80448c2ecf20Sopenharmony_ci
80458c2ecf20Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
80468c2ecf20Sopenharmony_ci		return -EIO;
80478c2ecf20Sopenharmony_ci	max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
80488c2ecf20Sopenharmony_ci	usp = READ_ONCE(net->ipv4.sysctl_ip_fwd_update_priority);
80498c2ecf20Sopenharmony_ci
80508c2ecf20Sopenharmony_ci	mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
80518c2ecf20Sopenharmony_ci	mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
80528c2ecf20Sopenharmony_ci	mlxsw_reg_rgcr_usp_set(rgcr_pl, usp);
80538c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
80548c2ecf20Sopenharmony_ci}
80558c2ecf20Sopenharmony_ci
80568c2ecf20Sopenharmony_cistatic void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
80578c2ecf20Sopenharmony_ci{
80588c2ecf20Sopenharmony_ci	char rgcr_pl[MLXSW_REG_RGCR_LEN];
80598c2ecf20Sopenharmony_ci
80608c2ecf20Sopenharmony_ci	mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
80618c2ecf20Sopenharmony_ci	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
80628c2ecf20Sopenharmony_ci}
80638c2ecf20Sopenharmony_ci
80648c2ecf20Sopenharmony_ciint mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,
80658c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
80668c2ecf20Sopenharmony_ci{
80678c2ecf20Sopenharmony_ci	struct mlxsw_sp_router *router;
80688c2ecf20Sopenharmony_ci	int err;
80698c2ecf20Sopenharmony_ci
80708c2ecf20Sopenharmony_ci	router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
80718c2ecf20Sopenharmony_ci	if (!router)
80728c2ecf20Sopenharmony_ci		return -ENOMEM;
80738c2ecf20Sopenharmony_ci	mutex_init(&router->lock);
80748c2ecf20Sopenharmony_ci	mlxsw_sp->router = router;
80758c2ecf20Sopenharmony_ci	router->mlxsw_sp = mlxsw_sp;
80768c2ecf20Sopenharmony_ci
80778c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
80788c2ecf20Sopenharmony_ci	err = __mlxsw_sp_router_init(mlxsw_sp);
80798c2ecf20Sopenharmony_ci	if (err)
80808c2ecf20Sopenharmony_ci		goto err_router_init;
80818c2ecf20Sopenharmony_ci
80828c2ecf20Sopenharmony_ci	err = mlxsw_sp_rifs_init(mlxsw_sp);
80838c2ecf20Sopenharmony_ci	if (err)
80848c2ecf20Sopenharmony_ci		goto err_rifs_init;
80858c2ecf20Sopenharmony_ci
80868c2ecf20Sopenharmony_ci	err = mlxsw_sp_ipips_init(mlxsw_sp);
80878c2ecf20Sopenharmony_ci	if (err)
80888c2ecf20Sopenharmony_ci		goto err_ipips_init;
80898c2ecf20Sopenharmony_ci
80908c2ecf20Sopenharmony_ci	err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
80918c2ecf20Sopenharmony_ci			      &mlxsw_sp_nexthop_ht_params);
80928c2ecf20Sopenharmony_ci	if (err)
80938c2ecf20Sopenharmony_ci		goto err_nexthop_ht_init;
80948c2ecf20Sopenharmony_ci
80958c2ecf20Sopenharmony_ci	err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
80968c2ecf20Sopenharmony_ci			      &mlxsw_sp_nexthop_group_ht_params);
80978c2ecf20Sopenharmony_ci	if (err)
80988c2ecf20Sopenharmony_ci		goto err_nexthop_group_ht_init;
80998c2ecf20Sopenharmony_ci
81008c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_list);
81018c2ecf20Sopenharmony_ci	err = mlxsw_sp_lpm_init(mlxsw_sp);
81028c2ecf20Sopenharmony_ci	if (err)
81038c2ecf20Sopenharmony_ci		goto err_lpm_init;
81048c2ecf20Sopenharmony_ci
81058c2ecf20Sopenharmony_ci	err = mlxsw_sp_mr_init(mlxsw_sp, &mlxsw_sp_mr_tcam_ops);
81068c2ecf20Sopenharmony_ci	if (err)
81078c2ecf20Sopenharmony_ci		goto err_mr_init;
81088c2ecf20Sopenharmony_ci
81098c2ecf20Sopenharmony_ci	err = mlxsw_sp_vrs_init(mlxsw_sp);
81108c2ecf20Sopenharmony_ci	if (err)
81118c2ecf20Sopenharmony_ci		goto err_vrs_init;
81128c2ecf20Sopenharmony_ci
81138c2ecf20Sopenharmony_ci	err = mlxsw_sp_neigh_init(mlxsw_sp);
81148c2ecf20Sopenharmony_ci	if (err)
81158c2ecf20Sopenharmony_ci		goto err_neigh_init;
81168c2ecf20Sopenharmony_ci
81178c2ecf20Sopenharmony_ci	err = mlxsw_sp_mp_hash_init(mlxsw_sp);
81188c2ecf20Sopenharmony_ci	if (err)
81198c2ecf20Sopenharmony_ci		goto err_mp_hash_init;
81208c2ecf20Sopenharmony_ci
81218c2ecf20Sopenharmony_ci	err = mlxsw_sp_dscp_init(mlxsw_sp);
81228c2ecf20Sopenharmony_ci	if (err)
81238c2ecf20Sopenharmony_ci		goto err_dscp_init;
81248c2ecf20Sopenharmony_ci
81258c2ecf20Sopenharmony_ci	router->inetaddr_nb.notifier_call = mlxsw_sp_inetaddr_event;
81268c2ecf20Sopenharmony_ci	err = register_inetaddr_notifier(&router->inetaddr_nb);
81278c2ecf20Sopenharmony_ci	if (err)
81288c2ecf20Sopenharmony_ci		goto err_register_inetaddr_notifier;
81298c2ecf20Sopenharmony_ci
81308c2ecf20Sopenharmony_ci	router->inet6addr_nb.notifier_call = mlxsw_sp_inet6addr_event;
81318c2ecf20Sopenharmony_ci	err = register_inet6addr_notifier(&router->inet6addr_nb);
81328c2ecf20Sopenharmony_ci	if (err)
81338c2ecf20Sopenharmony_ci		goto err_register_inet6addr_notifier;
81348c2ecf20Sopenharmony_ci
81358c2ecf20Sopenharmony_ci	mlxsw_sp->router->netevent_nb.notifier_call =
81368c2ecf20Sopenharmony_ci		mlxsw_sp_router_netevent_event;
81378c2ecf20Sopenharmony_ci	err = register_netevent_notifier(&mlxsw_sp->router->netevent_nb);
81388c2ecf20Sopenharmony_ci	if (err)
81398c2ecf20Sopenharmony_ci		goto err_register_netevent_notifier;
81408c2ecf20Sopenharmony_ci
81418c2ecf20Sopenharmony_ci	mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
81428c2ecf20Sopenharmony_ci	err = register_fib_notifier(mlxsw_sp_net(mlxsw_sp),
81438c2ecf20Sopenharmony_ci				    &mlxsw_sp->router->fib_nb,
81448c2ecf20Sopenharmony_ci				    mlxsw_sp_router_fib_dump_flush, extack);
81458c2ecf20Sopenharmony_ci	if (err)
81468c2ecf20Sopenharmony_ci		goto err_register_fib_notifier;
81478c2ecf20Sopenharmony_ci
81488c2ecf20Sopenharmony_ci	return 0;
81498c2ecf20Sopenharmony_ci
81508c2ecf20Sopenharmony_cierr_register_fib_notifier:
81518c2ecf20Sopenharmony_ci	unregister_netevent_notifier(&mlxsw_sp->router->netevent_nb);
81528c2ecf20Sopenharmony_cierr_register_netevent_notifier:
81538c2ecf20Sopenharmony_ci	unregister_inet6addr_notifier(&router->inet6addr_nb);
81548c2ecf20Sopenharmony_cierr_register_inet6addr_notifier:
81558c2ecf20Sopenharmony_ci	unregister_inetaddr_notifier(&router->inetaddr_nb);
81568c2ecf20Sopenharmony_cierr_register_inetaddr_notifier:
81578c2ecf20Sopenharmony_ci	mlxsw_core_flush_owq();
81588c2ecf20Sopenharmony_cierr_dscp_init:
81598c2ecf20Sopenharmony_cierr_mp_hash_init:
81608c2ecf20Sopenharmony_ci	mlxsw_sp_neigh_fini(mlxsw_sp);
81618c2ecf20Sopenharmony_cierr_neigh_init:
81628c2ecf20Sopenharmony_ci	mlxsw_sp_vrs_fini(mlxsw_sp);
81638c2ecf20Sopenharmony_cierr_vrs_init:
81648c2ecf20Sopenharmony_ci	mlxsw_sp_mr_fini(mlxsw_sp);
81658c2ecf20Sopenharmony_cierr_mr_init:
81668c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_fini(mlxsw_sp);
81678c2ecf20Sopenharmony_cierr_lpm_init:
81688c2ecf20Sopenharmony_ci	rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
81698c2ecf20Sopenharmony_cierr_nexthop_group_ht_init:
81708c2ecf20Sopenharmony_ci	rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
81718c2ecf20Sopenharmony_cierr_nexthop_ht_init:
81728c2ecf20Sopenharmony_ci	mlxsw_sp_ipips_fini(mlxsw_sp);
81738c2ecf20Sopenharmony_cierr_ipips_init:
81748c2ecf20Sopenharmony_ci	mlxsw_sp_rifs_fini(mlxsw_sp);
81758c2ecf20Sopenharmony_cierr_rifs_init:
81768c2ecf20Sopenharmony_ci	__mlxsw_sp_router_fini(mlxsw_sp);
81778c2ecf20Sopenharmony_cierr_router_init:
81788c2ecf20Sopenharmony_ci	mutex_destroy(&mlxsw_sp->router->lock);
81798c2ecf20Sopenharmony_ci	kfree(mlxsw_sp->router);
81808c2ecf20Sopenharmony_ci	return err;
81818c2ecf20Sopenharmony_ci}
81828c2ecf20Sopenharmony_ci
81838c2ecf20Sopenharmony_civoid mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
81848c2ecf20Sopenharmony_ci{
81858c2ecf20Sopenharmony_ci	unregister_fib_notifier(mlxsw_sp_net(mlxsw_sp),
81868c2ecf20Sopenharmony_ci				&mlxsw_sp->router->fib_nb);
81878c2ecf20Sopenharmony_ci	unregister_netevent_notifier(&mlxsw_sp->router->netevent_nb);
81888c2ecf20Sopenharmony_ci	unregister_inet6addr_notifier(&mlxsw_sp->router->inet6addr_nb);
81898c2ecf20Sopenharmony_ci	unregister_inetaddr_notifier(&mlxsw_sp->router->inetaddr_nb);
81908c2ecf20Sopenharmony_ci	mlxsw_core_flush_owq();
81918c2ecf20Sopenharmony_ci	mlxsw_sp_neigh_fini(mlxsw_sp);
81928c2ecf20Sopenharmony_ci	mlxsw_sp_vrs_fini(mlxsw_sp);
81938c2ecf20Sopenharmony_ci	mlxsw_sp_mr_fini(mlxsw_sp);
81948c2ecf20Sopenharmony_ci	mlxsw_sp_lpm_fini(mlxsw_sp);
81958c2ecf20Sopenharmony_ci	rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
81968c2ecf20Sopenharmony_ci	rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
81978c2ecf20Sopenharmony_ci	mlxsw_sp_ipips_fini(mlxsw_sp);
81988c2ecf20Sopenharmony_ci	mlxsw_sp_rifs_fini(mlxsw_sp);
81998c2ecf20Sopenharmony_ci	__mlxsw_sp_router_fini(mlxsw_sp);
82008c2ecf20Sopenharmony_ci	mutex_destroy(&mlxsw_sp->router->lock);
82018c2ecf20Sopenharmony_ci	kfree(mlxsw_sp->router);
82028c2ecf20Sopenharmony_ci}
8203