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(¶ms, mlxsw_sp_port_vlan); 66928c2ecf20Sopenharmony_ci rif = mlxsw_sp_rif_subport_get(mlxsw_sp, ¶ms, 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, ¶ms, 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