18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/types.h> 68c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 78c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 118c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 128c2ecf20Sopenharmony_ci#include <linux/if_bridge.h> 138c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 148c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 158c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 168c2ecf20Sopenharmony_ci#include <linux/netlink.h> 178c2ecf20Sopenharmony_ci#include <net/switchdev.h> 188c2ecf20Sopenharmony_ci#include <net/vxlan.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "spectrum_span.h" 218c2ecf20Sopenharmony_ci#include "spectrum_switchdev.h" 228c2ecf20Sopenharmony_ci#include "spectrum.h" 238c2ecf20Sopenharmony_ci#include "core.h" 248c2ecf20Sopenharmony_ci#include "reg.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct mlxsw_sp_bridge_ops; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct mlxsw_sp_bridge { 298c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 308c2ecf20Sopenharmony_ci struct { 318c2ecf20Sopenharmony_ci struct delayed_work dw; 328c2ecf20Sopenharmony_ci#define MLXSW_SP_DEFAULT_LEARNING_INTERVAL 100 338c2ecf20Sopenharmony_ci unsigned int interval; /* ms */ 348c2ecf20Sopenharmony_ci } fdb_notify; 358c2ecf20Sopenharmony_ci#define MLXSW_SP_MIN_AGEING_TIME 10 368c2ecf20Sopenharmony_ci#define MLXSW_SP_MAX_AGEING_TIME 1000000 378c2ecf20Sopenharmony_ci#define MLXSW_SP_DEFAULT_AGEING_TIME 300 388c2ecf20Sopenharmony_ci u32 ageing_time; 398c2ecf20Sopenharmony_ci bool vlan_enabled_exists; 408c2ecf20Sopenharmony_ci struct list_head bridges_list; 418c2ecf20Sopenharmony_ci DECLARE_BITMAP(mids_bitmap, MLXSW_SP_MID_MAX); 428c2ecf20Sopenharmony_ci const struct mlxsw_sp_bridge_ops *bridge_8021q_ops; 438c2ecf20Sopenharmony_ci const struct mlxsw_sp_bridge_ops *bridge_8021d_ops; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct mlxsw_sp_bridge_device { 478c2ecf20Sopenharmony_ci struct net_device *dev; 488c2ecf20Sopenharmony_ci struct list_head list; 498c2ecf20Sopenharmony_ci struct list_head ports_list; 508c2ecf20Sopenharmony_ci struct list_head mids_list; 518c2ecf20Sopenharmony_ci u8 vlan_enabled:1, 528c2ecf20Sopenharmony_ci multicast_enabled:1, 538c2ecf20Sopenharmony_ci mrouter:1; 548c2ecf20Sopenharmony_ci const struct mlxsw_sp_bridge_ops *ops; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct mlxsw_sp_bridge_port { 588c2ecf20Sopenharmony_ci struct net_device *dev; 598c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 608c2ecf20Sopenharmony_ci struct list_head list; 618c2ecf20Sopenharmony_ci struct list_head vlans_list; 628c2ecf20Sopenharmony_ci unsigned int ref_count; 638c2ecf20Sopenharmony_ci u8 stp_state; 648c2ecf20Sopenharmony_ci unsigned long flags; 658c2ecf20Sopenharmony_ci bool mrouter; 668c2ecf20Sopenharmony_ci bool lagged; 678c2ecf20Sopenharmony_ci union { 688c2ecf20Sopenharmony_ci u16 lag_id; 698c2ecf20Sopenharmony_ci u16 system_port; 708c2ecf20Sopenharmony_ci }; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistruct mlxsw_sp_bridge_vlan { 748c2ecf20Sopenharmony_ci struct list_head list; 758c2ecf20Sopenharmony_ci struct list_head port_vlan_list; 768c2ecf20Sopenharmony_ci u16 vid; 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistruct mlxsw_sp_bridge_ops { 808c2ecf20Sopenharmony_ci int (*port_join)(struct mlxsw_sp_bridge_device *bridge_device, 818c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 828c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 838c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack); 848c2ecf20Sopenharmony_ci void (*port_leave)(struct mlxsw_sp_bridge_device *bridge_device, 858c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 868c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port); 878c2ecf20Sopenharmony_ci int (*vxlan_join)(struct mlxsw_sp_bridge_device *bridge_device, 888c2ecf20Sopenharmony_ci const struct net_device *vxlan_dev, u16 vid, 898c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack); 908c2ecf20Sopenharmony_ci struct mlxsw_sp_fid * 918c2ecf20Sopenharmony_ci (*fid_get)(struct mlxsw_sp_bridge_device *bridge_device, 928c2ecf20Sopenharmony_ci u16 vid, struct netlink_ext_ack *extack); 938c2ecf20Sopenharmony_ci struct mlxsw_sp_fid * 948c2ecf20Sopenharmony_ci (*fid_lookup)(struct mlxsw_sp_bridge_device *bridge_device, 958c2ecf20Sopenharmony_ci u16 vid); 968c2ecf20Sopenharmony_ci u16 (*fid_vid)(struct mlxsw_sp_bridge_device *bridge_device, 978c2ecf20Sopenharmony_ci const struct mlxsw_sp_fid *fid); 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int 1018c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_fdb_flush(struct mlxsw_sp *mlxsw_sp, 1028c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 1038c2ecf20Sopenharmony_ci u16 fid_index); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void 1068c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, 1078c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void 1108c2ecf20Sopenharmony_cimlxsw_sp_bridge_mdb_mc_enable_sync(struct mlxsw_sp_port *mlxsw_sp_port, 1118c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device 1128c2ecf20Sopenharmony_ci *bridge_device); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void 1158c2ecf20Sopenharmony_cimlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port, 1168c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 1178c2ecf20Sopenharmony_ci bool add); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_device * 1208c2ecf20Sopenharmony_cimlxsw_sp_bridge_device_find(const struct mlxsw_sp_bridge *bridge, 1218c2ecf20Sopenharmony_ci const struct net_device *br_dev) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci list_for_each_entry(bridge_device, &bridge->bridges_list, list) 1268c2ecf20Sopenharmony_ci if (bridge_device->dev == br_dev) 1278c2ecf20Sopenharmony_ci return bridge_device; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return NULL; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cibool mlxsw_sp_bridge_device_is_offloaded(const struct mlxsw_sp *mlxsw_sp, 1338c2ecf20Sopenharmony_ci const struct net_device *br_dev) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci return !!mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int mlxsw_sp_bridge_device_upper_rif_destroy(struct net_device *dev, 1398c2ecf20Sopenharmony_ci struct netdev_nested_priv *priv) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = priv->data; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci mlxsw_sp_rif_destroy_by_dev(mlxsw_sp, dev); 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void mlxsw_sp_bridge_device_rifs_destroy(struct mlxsw_sp *mlxsw_sp, 1488c2ecf20Sopenharmony_ci struct net_device *dev) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct netdev_nested_priv priv = { 1518c2ecf20Sopenharmony_ci .data = (void *)mlxsw_sp, 1528c2ecf20Sopenharmony_ci }; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci mlxsw_sp_rif_destroy_by_dev(mlxsw_sp, dev); 1558c2ecf20Sopenharmony_ci netdev_walk_all_upper_dev_rcu(dev, 1568c2ecf20Sopenharmony_ci mlxsw_sp_bridge_device_upper_rif_destroy, 1578c2ecf20Sopenharmony_ci &priv); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int mlxsw_sp_bridge_device_vxlan_init(struct mlxsw_sp_bridge *bridge, 1618c2ecf20Sopenharmony_ci struct net_device *br_dev, 1628c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct net_device *dev, *stop_dev; 1658c2ecf20Sopenharmony_ci struct list_head *iter; 1668c2ecf20Sopenharmony_ci int err; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci netdev_for_each_lower_dev(br_dev, dev, iter) { 1698c2ecf20Sopenharmony_ci if (netif_is_vxlan(dev) && netif_running(dev)) { 1708c2ecf20Sopenharmony_ci err = mlxsw_sp_bridge_vxlan_join(bridge->mlxsw_sp, 1718c2ecf20Sopenharmony_ci br_dev, dev, 0, 1728c2ecf20Sopenharmony_ci extack); 1738c2ecf20Sopenharmony_ci if (err) { 1748c2ecf20Sopenharmony_ci stop_dev = dev; 1758c2ecf20Sopenharmony_ci goto err_vxlan_join; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci return 0; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cierr_vxlan_join: 1838c2ecf20Sopenharmony_ci netdev_for_each_lower_dev(br_dev, dev, iter) { 1848c2ecf20Sopenharmony_ci if (netif_is_vxlan(dev) && netif_running(dev)) { 1858c2ecf20Sopenharmony_ci if (stop_dev == dev) 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci mlxsw_sp_bridge_vxlan_leave(bridge->mlxsw_sp, dev); 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci return err; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic void mlxsw_sp_bridge_device_vxlan_fini(struct mlxsw_sp_bridge *bridge, 1948c2ecf20Sopenharmony_ci struct net_device *br_dev) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct net_device *dev; 1978c2ecf20Sopenharmony_ci struct list_head *iter; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci netdev_for_each_lower_dev(br_dev, dev, iter) { 2008c2ecf20Sopenharmony_ci if (netif_is_vxlan(dev) && netif_running(dev)) 2018c2ecf20Sopenharmony_ci mlxsw_sp_bridge_vxlan_leave(bridge->mlxsw_sp, dev); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_device * 2068c2ecf20Sopenharmony_cimlxsw_sp_bridge_device_create(struct mlxsw_sp_bridge *bridge, 2078c2ecf20Sopenharmony_ci struct net_device *br_dev, 2088c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct device *dev = bridge->mlxsw_sp->bus_info->dev; 2118c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 2128c2ecf20Sopenharmony_ci bool vlan_enabled = br_vlan_enabled(br_dev); 2138c2ecf20Sopenharmony_ci int err; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (vlan_enabled && bridge->vlan_enabled_exists) { 2168c2ecf20Sopenharmony_ci dev_err(dev, "Only one VLAN-aware bridge is supported\n"); 2178c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Only one VLAN-aware bridge is supported"); 2188c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci bridge_device = kzalloc(sizeof(*bridge_device), GFP_KERNEL); 2228c2ecf20Sopenharmony_ci if (!bridge_device) 2238c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci bridge_device->dev = br_dev; 2268c2ecf20Sopenharmony_ci bridge_device->vlan_enabled = vlan_enabled; 2278c2ecf20Sopenharmony_ci bridge_device->multicast_enabled = br_multicast_enabled(br_dev); 2288c2ecf20Sopenharmony_ci bridge_device->mrouter = br_multicast_router(br_dev); 2298c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bridge_device->ports_list); 2308c2ecf20Sopenharmony_ci if (vlan_enabled) { 2318c2ecf20Sopenharmony_ci bridge->vlan_enabled_exists = true; 2328c2ecf20Sopenharmony_ci bridge_device->ops = bridge->bridge_8021q_ops; 2338c2ecf20Sopenharmony_ci } else { 2348c2ecf20Sopenharmony_ci bridge_device->ops = bridge->bridge_8021d_ops; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bridge_device->mids_list); 2378c2ecf20Sopenharmony_ci list_add(&bridge_device->list, &bridge->bridges_list); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* It is possible we already have VXLAN devices enslaved to the bridge. 2408c2ecf20Sopenharmony_ci * In which case, we need to replay their configuration as if they were 2418c2ecf20Sopenharmony_ci * just now enslaved to the bridge. 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_ci err = mlxsw_sp_bridge_device_vxlan_init(bridge, br_dev, extack); 2448c2ecf20Sopenharmony_ci if (err) 2458c2ecf20Sopenharmony_ci goto err_vxlan_init; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return bridge_device; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cierr_vxlan_init: 2508c2ecf20Sopenharmony_ci list_del(&bridge_device->list); 2518c2ecf20Sopenharmony_ci if (bridge_device->vlan_enabled) 2528c2ecf20Sopenharmony_ci bridge->vlan_enabled_exists = false; 2538c2ecf20Sopenharmony_ci kfree(bridge_device); 2548c2ecf20Sopenharmony_ci return ERR_PTR(err); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void 2588c2ecf20Sopenharmony_cimlxsw_sp_bridge_device_destroy(struct mlxsw_sp_bridge *bridge, 2598c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci mlxsw_sp_bridge_device_vxlan_fini(bridge, bridge_device->dev); 2628c2ecf20Sopenharmony_ci mlxsw_sp_bridge_device_rifs_destroy(bridge->mlxsw_sp, 2638c2ecf20Sopenharmony_ci bridge_device->dev); 2648c2ecf20Sopenharmony_ci list_del(&bridge_device->list); 2658c2ecf20Sopenharmony_ci if (bridge_device->vlan_enabled) 2668c2ecf20Sopenharmony_ci bridge->vlan_enabled_exists = false; 2678c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&bridge_device->ports_list)); 2688c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&bridge_device->mids_list)); 2698c2ecf20Sopenharmony_ci kfree(bridge_device); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_device * 2738c2ecf20Sopenharmony_cimlxsw_sp_bridge_device_get(struct mlxsw_sp_bridge *bridge, 2748c2ecf20Sopenharmony_ci struct net_device *br_dev, 2758c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(bridge, br_dev); 2808c2ecf20Sopenharmony_ci if (bridge_device) 2818c2ecf20Sopenharmony_ci return bridge_device; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return mlxsw_sp_bridge_device_create(bridge, br_dev, extack); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic void 2878c2ecf20Sopenharmony_cimlxsw_sp_bridge_device_put(struct mlxsw_sp_bridge *bridge, 2888c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci if (list_empty(&bridge_device->ports_list)) 2918c2ecf20Sopenharmony_ci mlxsw_sp_bridge_device_destroy(bridge, bridge_device); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_port * 2958c2ecf20Sopenharmony_ci__mlxsw_sp_bridge_port_find(const struct mlxsw_sp_bridge_device *bridge_device, 2968c2ecf20Sopenharmony_ci const struct net_device *brport_dev) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci list_for_each_entry(bridge_port, &bridge_device->ports_list, list) { 3018c2ecf20Sopenharmony_ci if (bridge_port->dev == brport_dev) 3028c2ecf20Sopenharmony_ci return bridge_port; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return NULL; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistruct mlxsw_sp_bridge_port * 3098c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_find(struct mlxsw_sp_bridge *bridge, 3108c2ecf20Sopenharmony_ci struct net_device *brport_dev) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct net_device *br_dev = netdev_master_upper_dev_get(brport_dev); 3138c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (!br_dev) 3168c2ecf20Sopenharmony_ci return NULL; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(bridge, br_dev); 3198c2ecf20Sopenharmony_ci if (!bridge_device) 3208c2ecf20Sopenharmony_ci return NULL; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return __mlxsw_sp_bridge_port_find(bridge_device, brport_dev); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_port * 3268c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_create(struct mlxsw_sp_bridge_device *bridge_device, 3278c2ecf20Sopenharmony_ci struct net_device *brport_dev) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 3308c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci bridge_port = kzalloc(sizeof(*bridge_port), GFP_KERNEL); 3338c2ecf20Sopenharmony_ci if (!bridge_port) 3348c2ecf20Sopenharmony_ci return NULL; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(brport_dev); 3378c2ecf20Sopenharmony_ci bridge_port->lagged = mlxsw_sp_port->lagged; 3388c2ecf20Sopenharmony_ci if (bridge_port->lagged) 3398c2ecf20Sopenharmony_ci bridge_port->lag_id = mlxsw_sp_port->lag_id; 3408c2ecf20Sopenharmony_ci else 3418c2ecf20Sopenharmony_ci bridge_port->system_port = mlxsw_sp_port->local_port; 3428c2ecf20Sopenharmony_ci bridge_port->dev = brport_dev; 3438c2ecf20Sopenharmony_ci bridge_port->bridge_device = bridge_device; 3448c2ecf20Sopenharmony_ci bridge_port->stp_state = BR_STATE_DISABLED; 3458c2ecf20Sopenharmony_ci bridge_port->flags = BR_LEARNING | BR_FLOOD | BR_LEARNING_SYNC | 3468c2ecf20Sopenharmony_ci BR_MCAST_FLOOD; 3478c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bridge_port->vlans_list); 3488c2ecf20Sopenharmony_ci list_add(&bridge_port->list, &bridge_device->ports_list); 3498c2ecf20Sopenharmony_ci bridge_port->ref_count = 1; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return bridge_port; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic void 3558c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_destroy(struct mlxsw_sp_bridge_port *bridge_port) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci list_del(&bridge_port->list); 3588c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&bridge_port->vlans_list)); 3598c2ecf20Sopenharmony_ci kfree(bridge_port); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_port * 3638c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_get(struct mlxsw_sp_bridge *bridge, 3648c2ecf20Sopenharmony_ci struct net_device *brport_dev, 3658c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct net_device *br_dev = netdev_master_upper_dev_get(brport_dev); 3688c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 3698c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 3708c2ecf20Sopenharmony_ci int err; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(bridge, brport_dev); 3738c2ecf20Sopenharmony_ci if (bridge_port) { 3748c2ecf20Sopenharmony_ci bridge_port->ref_count++; 3758c2ecf20Sopenharmony_ci return bridge_port; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_get(bridge, br_dev, extack); 3798c2ecf20Sopenharmony_ci if (IS_ERR(bridge_device)) 3808c2ecf20Sopenharmony_ci return ERR_CAST(bridge_device); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_create(bridge_device, brport_dev); 3838c2ecf20Sopenharmony_ci if (!bridge_port) { 3848c2ecf20Sopenharmony_ci err = -ENOMEM; 3858c2ecf20Sopenharmony_ci goto err_bridge_port_create; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return bridge_port; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cierr_bridge_port_create: 3918c2ecf20Sopenharmony_ci mlxsw_sp_bridge_device_put(bridge, bridge_device); 3928c2ecf20Sopenharmony_ci return ERR_PTR(err); 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic void mlxsw_sp_bridge_port_put(struct mlxsw_sp_bridge *bridge, 3968c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (--bridge_port->ref_count != 0) 4018c2ecf20Sopenharmony_ci return; 4028c2ecf20Sopenharmony_ci bridge_device = bridge_port->bridge_device; 4038c2ecf20Sopenharmony_ci mlxsw_sp_bridge_port_destroy(bridge_port); 4048c2ecf20Sopenharmony_ci mlxsw_sp_bridge_device_put(bridge, bridge_device); 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic struct mlxsw_sp_port_vlan * 4088c2ecf20Sopenharmony_cimlxsw_sp_port_vlan_find_by_bridge(struct mlxsw_sp_port *mlxsw_sp_port, 4098c2ecf20Sopenharmony_ci const struct mlxsw_sp_bridge_device * 4108c2ecf20Sopenharmony_ci bridge_device, 4118c2ecf20Sopenharmony_ci u16 vid) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list, 4168c2ecf20Sopenharmony_ci list) { 4178c2ecf20Sopenharmony_ci if (!mlxsw_sp_port_vlan->bridge_port) 4188c2ecf20Sopenharmony_ci continue; 4198c2ecf20Sopenharmony_ci if (mlxsw_sp_port_vlan->bridge_port->bridge_device != 4208c2ecf20Sopenharmony_ci bridge_device) 4218c2ecf20Sopenharmony_ci continue; 4228c2ecf20Sopenharmony_ci if (bridge_device->vlan_enabled && 4238c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan->vid != vid) 4248c2ecf20Sopenharmony_ci continue; 4258c2ecf20Sopenharmony_ci return mlxsw_sp_port_vlan; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return NULL; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic struct mlxsw_sp_port_vlan* 4328c2ecf20Sopenharmony_cimlxsw_sp_port_vlan_find_by_fid(struct mlxsw_sp_port *mlxsw_sp_port, 4338c2ecf20Sopenharmony_ci u16 fid_index) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list, 4388c2ecf20Sopenharmony_ci list) { 4398c2ecf20Sopenharmony_ci struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (fid && mlxsw_sp_fid_index(fid) == fid_index) 4428c2ecf20Sopenharmony_ci return mlxsw_sp_port_vlan; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci return NULL; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_vlan * 4498c2ecf20Sopenharmony_cimlxsw_sp_bridge_vlan_find(const struct mlxsw_sp_bridge_port *bridge_port, 4508c2ecf20Sopenharmony_ci u16 vid) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) { 4558c2ecf20Sopenharmony_ci if (bridge_vlan->vid == vid) 4568c2ecf20Sopenharmony_ci return bridge_vlan; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci return NULL; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_vlan * 4638c2ecf20Sopenharmony_cimlxsw_sp_bridge_vlan_create(struct mlxsw_sp_bridge_port *bridge_port, u16 vid) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci bridge_vlan = kzalloc(sizeof(*bridge_vlan), GFP_KERNEL); 4688c2ecf20Sopenharmony_ci if (!bridge_vlan) 4698c2ecf20Sopenharmony_ci return NULL; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bridge_vlan->port_vlan_list); 4728c2ecf20Sopenharmony_ci bridge_vlan->vid = vid; 4738c2ecf20Sopenharmony_ci list_add(&bridge_vlan->list, &bridge_port->vlans_list); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return bridge_vlan; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic void 4798c2ecf20Sopenharmony_cimlxsw_sp_bridge_vlan_destroy(struct mlxsw_sp_bridge_vlan *bridge_vlan) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci list_del(&bridge_vlan->list); 4828c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&bridge_vlan->port_vlan_list)); 4838c2ecf20Sopenharmony_ci kfree(bridge_vlan); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic struct mlxsw_sp_bridge_vlan * 4878c2ecf20Sopenharmony_cimlxsw_sp_bridge_vlan_get(struct mlxsw_sp_bridge_port *bridge_port, u16 vid) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci bridge_vlan = mlxsw_sp_bridge_vlan_find(bridge_port, vid); 4928c2ecf20Sopenharmony_ci if (bridge_vlan) 4938c2ecf20Sopenharmony_ci return bridge_vlan; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return mlxsw_sp_bridge_vlan_create(bridge_port, vid); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic void mlxsw_sp_bridge_vlan_put(struct mlxsw_sp_bridge_vlan *bridge_vlan) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci if (list_empty(&bridge_vlan->port_vlan_list)) 5018c2ecf20Sopenharmony_ci mlxsw_sp_bridge_vlan_destroy(bridge_vlan); 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic int 5058c2ecf20Sopenharmony_cimlxsw_sp_port_bridge_vlan_stp_set(struct mlxsw_sp_port *mlxsw_sp_port, 5068c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan, 5078c2ecf20Sopenharmony_ci u8 state) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci list_for_each_entry(mlxsw_sp_port_vlan, &bridge_vlan->port_vlan_list, 5128c2ecf20Sopenharmony_ci bridge_vlan_node) { 5138c2ecf20Sopenharmony_ci if (mlxsw_sp_port_vlan->mlxsw_sp_port != mlxsw_sp_port) 5148c2ecf20Sopenharmony_ci continue; 5158c2ecf20Sopenharmony_ci return mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, 5168c2ecf20Sopenharmony_ci bridge_vlan->vid, state); 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port, 5238c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 5248c2ecf20Sopenharmony_ci struct net_device *orig_dev, 5258c2ecf20Sopenharmony_ci u8 state) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 5288c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 5298c2ecf20Sopenharmony_ci int err; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* It's possible we failed to enslave the port, yet this 5358c2ecf20Sopenharmony_ci * operation is executed due to it being deferred. 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge, 5388c2ecf20Sopenharmony_ci orig_dev); 5398c2ecf20Sopenharmony_ci if (!bridge_port) 5408c2ecf20Sopenharmony_ci return 0; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) { 5438c2ecf20Sopenharmony_ci err = mlxsw_sp_port_bridge_vlan_stp_set(mlxsw_sp_port, 5448c2ecf20Sopenharmony_ci bridge_vlan, state); 5458c2ecf20Sopenharmony_ci if (err) 5468c2ecf20Sopenharmony_ci goto err_port_bridge_vlan_stp_set; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci bridge_port->stp_state = state; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cierr_port_bridge_vlan_stp_set: 5548c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(bridge_vlan, 5558c2ecf20Sopenharmony_ci &bridge_port->vlans_list, list) 5568c2ecf20Sopenharmony_ci mlxsw_sp_port_bridge_vlan_stp_set(mlxsw_sp_port, bridge_vlan, 5578c2ecf20Sopenharmony_ci bridge_port->stp_state); 5588c2ecf20Sopenharmony_ci return err; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic int 5628c2ecf20Sopenharmony_cimlxsw_sp_port_bridge_vlan_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, 5638c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan, 5648c2ecf20Sopenharmony_ci enum mlxsw_sp_flood_type packet_type, 5658c2ecf20Sopenharmony_ci bool member) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci list_for_each_entry(mlxsw_sp_port_vlan, &bridge_vlan->port_vlan_list, 5708c2ecf20Sopenharmony_ci bridge_vlan_node) { 5718c2ecf20Sopenharmony_ci if (mlxsw_sp_port_vlan->mlxsw_sp_port != mlxsw_sp_port) 5728c2ecf20Sopenharmony_ci continue; 5738c2ecf20Sopenharmony_ci return mlxsw_sp_fid_flood_set(mlxsw_sp_port_vlan->fid, 5748c2ecf20Sopenharmony_ci packet_type, 5758c2ecf20Sopenharmony_ci mlxsw_sp_port->local_port, 5768c2ecf20Sopenharmony_ci member); 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci return 0; 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistatic int 5838c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_flood_table_set(struct mlxsw_sp_port *mlxsw_sp_port, 5848c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 5858c2ecf20Sopenharmony_ci enum mlxsw_sp_flood_type packet_type, 5868c2ecf20Sopenharmony_ci bool member) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 5898c2ecf20Sopenharmony_ci int err; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) { 5928c2ecf20Sopenharmony_ci err = mlxsw_sp_port_bridge_vlan_flood_set(mlxsw_sp_port, 5938c2ecf20Sopenharmony_ci bridge_vlan, 5948c2ecf20Sopenharmony_ci packet_type, 5958c2ecf20Sopenharmony_ci member); 5968c2ecf20Sopenharmony_ci if (err) 5978c2ecf20Sopenharmony_ci goto err_port_bridge_vlan_flood_set; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci return 0; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cierr_port_bridge_vlan_flood_set: 6038c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(bridge_vlan, 6048c2ecf20Sopenharmony_ci &bridge_port->vlans_list, list) 6058c2ecf20Sopenharmony_ci mlxsw_sp_port_bridge_vlan_flood_set(mlxsw_sp_port, bridge_vlan, 6068c2ecf20Sopenharmony_ci packet_type, !member); 6078c2ecf20Sopenharmony_ci return err; 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic int 6118c2ecf20Sopenharmony_cimlxsw_sp_port_bridge_vlan_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, 6128c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan, 6138c2ecf20Sopenharmony_ci bool set) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 6168c2ecf20Sopenharmony_ci u16 vid = bridge_vlan->vid; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci list_for_each_entry(mlxsw_sp_port_vlan, &bridge_vlan->port_vlan_list, 6198c2ecf20Sopenharmony_ci bridge_vlan_node) { 6208c2ecf20Sopenharmony_ci if (mlxsw_sp_port_vlan->mlxsw_sp_port != mlxsw_sp_port) 6218c2ecf20Sopenharmony_ci continue; 6228c2ecf20Sopenharmony_ci return mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, set); 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci return 0; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic int 6298c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, 6308c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 6318c2ecf20Sopenharmony_ci bool set) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 6348c2ecf20Sopenharmony_ci int err; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) { 6378c2ecf20Sopenharmony_ci err = mlxsw_sp_port_bridge_vlan_learning_set(mlxsw_sp_port, 6388c2ecf20Sopenharmony_ci bridge_vlan, set); 6398c2ecf20Sopenharmony_ci if (err) 6408c2ecf20Sopenharmony_ci goto err_port_bridge_vlan_learning_set; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci return 0; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cierr_port_bridge_vlan_learning_set: 6468c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(bridge_vlan, 6478c2ecf20Sopenharmony_ci &bridge_port->vlans_list, list) 6488c2ecf20Sopenharmony_ci mlxsw_sp_port_bridge_vlan_learning_set(mlxsw_sp_port, 6498c2ecf20Sopenharmony_ci bridge_vlan, !set); 6508c2ecf20Sopenharmony_ci return err; 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_attr_br_pre_flags_set(struct mlxsw_sp_port 6548c2ecf20Sopenharmony_ci *mlxsw_sp_port, 6558c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 6568c2ecf20Sopenharmony_ci unsigned long brport_flags) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci if (brport_flags & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) 6598c2ecf20Sopenharmony_ci return -EINVAL; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, 6658c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 6668c2ecf20Sopenharmony_ci struct net_device *orig_dev, 6678c2ecf20Sopenharmony_ci unsigned long brport_flags) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 6708c2ecf20Sopenharmony_ci int err; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) 6738c2ecf20Sopenharmony_ci return 0; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge, 6768c2ecf20Sopenharmony_ci orig_dev); 6778c2ecf20Sopenharmony_ci if (!bridge_port) 6788c2ecf20Sopenharmony_ci return 0; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port, 6818c2ecf20Sopenharmony_ci MLXSW_SP_FLOOD_TYPE_UC, 6828c2ecf20Sopenharmony_ci brport_flags & BR_FLOOD); 6838c2ecf20Sopenharmony_ci if (err) 6848c2ecf20Sopenharmony_ci return err; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci err = mlxsw_sp_bridge_port_learning_set(mlxsw_sp_port, bridge_port, 6878c2ecf20Sopenharmony_ci brport_flags & BR_LEARNING); 6888c2ecf20Sopenharmony_ci if (err) 6898c2ecf20Sopenharmony_ci return err; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (bridge_port->bridge_device->multicast_enabled) 6928c2ecf20Sopenharmony_ci goto out; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port, 6958c2ecf20Sopenharmony_ci MLXSW_SP_FLOOD_TYPE_MC, 6968c2ecf20Sopenharmony_ci brport_flags & 6978c2ecf20Sopenharmony_ci BR_MCAST_FLOOD); 6988c2ecf20Sopenharmony_ci if (err) 6998c2ecf20Sopenharmony_ci return err; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ciout: 7028c2ecf20Sopenharmony_ci memcpy(&bridge_port->flags, &brport_flags, sizeof(brport_flags)); 7038c2ecf20Sopenharmony_ci return 0; 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic int mlxsw_sp_ageing_set(struct mlxsw_sp *mlxsw_sp, u32 ageing_time) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci char sfdat_pl[MLXSW_REG_SFDAT_LEN]; 7098c2ecf20Sopenharmony_ci int err; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci mlxsw_reg_sfdat_pack(sfdat_pl, ageing_time); 7128c2ecf20Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdat), sfdat_pl); 7138c2ecf20Sopenharmony_ci if (err) 7148c2ecf20Sopenharmony_ci return err; 7158c2ecf20Sopenharmony_ci mlxsw_sp->bridge->ageing_time = ageing_time; 7168c2ecf20Sopenharmony_ci return 0; 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port, 7208c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 7218c2ecf20Sopenharmony_ci unsigned long ageing_clock_t) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 7248c2ecf20Sopenharmony_ci unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t); 7258c2ecf20Sopenharmony_ci u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) { 7288c2ecf20Sopenharmony_ci if (ageing_time < MLXSW_SP_MIN_AGEING_TIME || 7298c2ecf20Sopenharmony_ci ageing_time > MLXSW_SP_MAX_AGEING_TIME) 7308c2ecf20Sopenharmony_ci return -ERANGE; 7318c2ecf20Sopenharmony_ci else 7328c2ecf20Sopenharmony_ci return 0; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time); 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, 7398c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 7408c2ecf20Sopenharmony_ci struct net_device *orig_dev, 7418c2ecf20Sopenharmony_ci bool vlan_enabled) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 7448c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci if (!switchdev_trans_ph_prepare(trans)) 7478c2ecf20Sopenharmony_ci return 0; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev); 7508c2ecf20Sopenharmony_ci if (WARN_ON(!bridge_device)) 7518c2ecf20Sopenharmony_ci return -EINVAL; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci if (bridge_device->vlan_enabled == vlan_enabled) 7548c2ecf20Sopenharmony_ci return 0; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci netdev_err(bridge_device->dev, "VLAN filtering can't be changed for existing bridge\n"); 7578c2ecf20Sopenharmony_ci return -EINVAL; 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_attr_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port, 7618c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 7628c2ecf20Sopenharmony_ci struct net_device *orig_dev, 7638c2ecf20Sopenharmony_ci bool is_port_mrouter) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 7668c2ecf20Sopenharmony_ci int err; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) 7698c2ecf20Sopenharmony_ci return 0; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge, 7728c2ecf20Sopenharmony_ci orig_dev); 7738c2ecf20Sopenharmony_ci if (!bridge_port) 7748c2ecf20Sopenharmony_ci return 0; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci if (!bridge_port->bridge_device->multicast_enabled) 7778c2ecf20Sopenharmony_ci goto out; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port, 7808c2ecf20Sopenharmony_ci MLXSW_SP_FLOOD_TYPE_MC, 7818c2ecf20Sopenharmony_ci is_port_mrouter); 7828c2ecf20Sopenharmony_ci if (err) 7838c2ecf20Sopenharmony_ci return err; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci mlxsw_sp_port_mrouter_update_mdb(mlxsw_sp_port, bridge_port, 7868c2ecf20Sopenharmony_ci is_port_mrouter); 7878c2ecf20Sopenharmony_ciout: 7888c2ecf20Sopenharmony_ci bridge_port->mrouter = is_port_mrouter; 7898c2ecf20Sopenharmony_ci return 0; 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic bool mlxsw_sp_mc_flood(const struct mlxsw_sp_bridge_port *bridge_port) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci const struct mlxsw_sp_bridge_device *bridge_device; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci bridge_device = bridge_port->bridge_device; 7978c2ecf20Sopenharmony_ci return bridge_device->multicast_enabled ? bridge_port->mrouter : 7988c2ecf20Sopenharmony_ci bridge_port->flags & BR_MCAST_FLOOD; 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_mc_disabled_set(struct mlxsw_sp_port *mlxsw_sp_port, 8028c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 8038c2ecf20Sopenharmony_ci struct net_device *orig_dev, 8048c2ecf20Sopenharmony_ci bool mc_disabled) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 8078c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 8088c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 8098c2ecf20Sopenharmony_ci int err; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) 8128c2ecf20Sopenharmony_ci return 0; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* It's possible we failed to enslave the port, yet this 8158c2ecf20Sopenharmony_ci * operation is executed due to it being deferred. 8168c2ecf20Sopenharmony_ci */ 8178c2ecf20Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev); 8188c2ecf20Sopenharmony_ci if (!bridge_device) 8198c2ecf20Sopenharmony_ci return 0; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (bridge_device->multicast_enabled != !mc_disabled) { 8228c2ecf20Sopenharmony_ci bridge_device->multicast_enabled = !mc_disabled; 8238c2ecf20Sopenharmony_ci mlxsw_sp_bridge_mdb_mc_enable_sync(mlxsw_sp_port, 8248c2ecf20Sopenharmony_ci bridge_device); 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci list_for_each_entry(bridge_port, &bridge_device->ports_list, list) { 8288c2ecf20Sopenharmony_ci enum mlxsw_sp_flood_type packet_type = MLXSW_SP_FLOOD_TYPE_MC; 8298c2ecf20Sopenharmony_ci bool member = mlxsw_sp_mc_flood(bridge_port); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, 8328c2ecf20Sopenharmony_ci bridge_port, 8338c2ecf20Sopenharmony_ci packet_type, member); 8348c2ecf20Sopenharmony_ci if (err) 8358c2ecf20Sopenharmony_ci return err; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci bridge_device->multicast_enabled = !mc_disabled; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci return 0; 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic int mlxsw_sp_smid_router_port_set(struct mlxsw_sp *mlxsw_sp, 8448c2ecf20Sopenharmony_ci u16 mid_idx, bool add) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci char *smid_pl; 8478c2ecf20Sopenharmony_ci int err; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci smid_pl = kmalloc(MLXSW_REG_SMID_LEN, GFP_KERNEL); 8508c2ecf20Sopenharmony_ci if (!smid_pl) 8518c2ecf20Sopenharmony_ci return -ENOMEM; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci mlxsw_reg_smid_pack(smid_pl, mid_idx, 8548c2ecf20Sopenharmony_ci mlxsw_sp_router_port(mlxsw_sp), add); 8558c2ecf20Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid), smid_pl); 8568c2ecf20Sopenharmony_ci kfree(smid_pl); 8578c2ecf20Sopenharmony_ci return err; 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic void 8618c2ecf20Sopenharmony_cimlxsw_sp_bridge_mrouter_update_mdb(struct mlxsw_sp *mlxsw_sp, 8628c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 8638c2ecf20Sopenharmony_ci bool add) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci struct mlxsw_sp_mid *mid; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci list_for_each_entry(mid, &bridge_device->mids_list, list) 8688c2ecf20Sopenharmony_ci mlxsw_sp_smid_router_port_set(mlxsw_sp, mid->mid, add); 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic int 8728c2ecf20Sopenharmony_cimlxsw_sp_port_attr_br_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port, 8738c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 8748c2ecf20Sopenharmony_ci struct net_device *orig_dev, 8758c2ecf20Sopenharmony_ci bool is_mrouter) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 8788c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) 8818c2ecf20Sopenharmony_ci return 0; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci /* It's possible we failed to enslave the port, yet this 8848c2ecf20Sopenharmony_ci * operation is executed due to it being deferred. 8858c2ecf20Sopenharmony_ci */ 8868c2ecf20Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev); 8878c2ecf20Sopenharmony_ci if (!bridge_device) 8888c2ecf20Sopenharmony_ci return 0; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (bridge_device->mrouter != is_mrouter) 8918c2ecf20Sopenharmony_ci mlxsw_sp_bridge_mrouter_update_mdb(mlxsw_sp, bridge_device, 8928c2ecf20Sopenharmony_ci is_mrouter); 8938c2ecf20Sopenharmony_ci bridge_device->mrouter = is_mrouter; 8948c2ecf20Sopenharmony_ci return 0; 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_attr_set(struct net_device *dev, 8988c2ecf20Sopenharmony_ci const struct switchdev_attr *attr, 8998c2ecf20Sopenharmony_ci struct switchdev_trans *trans) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 9028c2ecf20Sopenharmony_ci int err; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci switch (attr->id) { 9058c2ecf20Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_STP_STATE: 9068c2ecf20Sopenharmony_ci err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port, trans, 9078c2ecf20Sopenharmony_ci attr->orig_dev, 9088c2ecf20Sopenharmony_ci attr->u.stp_state); 9098c2ecf20Sopenharmony_ci break; 9108c2ecf20Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: 9118c2ecf20Sopenharmony_ci err = mlxsw_sp_port_attr_br_pre_flags_set(mlxsw_sp_port, 9128c2ecf20Sopenharmony_ci trans, 9138c2ecf20Sopenharmony_ci attr->u.brport_flags); 9148c2ecf20Sopenharmony_ci break; 9158c2ecf20Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: 9168c2ecf20Sopenharmony_ci err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port, trans, 9178c2ecf20Sopenharmony_ci attr->orig_dev, 9188c2ecf20Sopenharmony_ci attr->u.brport_flags); 9198c2ecf20Sopenharmony_ci break; 9208c2ecf20Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: 9218c2ecf20Sopenharmony_ci err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, trans, 9228c2ecf20Sopenharmony_ci attr->u.ageing_time); 9238c2ecf20Sopenharmony_ci break; 9248c2ecf20Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: 9258c2ecf20Sopenharmony_ci err = mlxsw_sp_port_attr_br_vlan_set(mlxsw_sp_port, trans, 9268c2ecf20Sopenharmony_ci attr->orig_dev, 9278c2ecf20Sopenharmony_ci attr->u.vlan_filtering); 9288c2ecf20Sopenharmony_ci break; 9298c2ecf20Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_MROUTER: 9308c2ecf20Sopenharmony_ci err = mlxsw_sp_port_attr_mrouter_set(mlxsw_sp_port, trans, 9318c2ecf20Sopenharmony_ci attr->orig_dev, 9328c2ecf20Sopenharmony_ci attr->u.mrouter); 9338c2ecf20Sopenharmony_ci break; 9348c2ecf20Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED: 9358c2ecf20Sopenharmony_ci err = mlxsw_sp_port_mc_disabled_set(mlxsw_sp_port, trans, 9368c2ecf20Sopenharmony_ci attr->orig_dev, 9378c2ecf20Sopenharmony_ci attr->u.mc_disabled); 9388c2ecf20Sopenharmony_ci break; 9398c2ecf20Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER: 9408c2ecf20Sopenharmony_ci err = mlxsw_sp_port_attr_br_mrouter_set(mlxsw_sp_port, trans, 9418c2ecf20Sopenharmony_ci attr->orig_dev, 9428c2ecf20Sopenharmony_ci attr->u.mrouter); 9438c2ecf20Sopenharmony_ci break; 9448c2ecf20Sopenharmony_ci default: 9458c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 9468c2ecf20Sopenharmony_ci break; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci if (switchdev_trans_ph_commit(trans)) 9508c2ecf20Sopenharmony_ci mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci return err; 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cistatic int 9568c2ecf20Sopenharmony_cimlxsw_sp_port_vlan_fid_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan, 9578c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 9588c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 9598c2ecf20Sopenharmony_ci{ 9608c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port; 9618c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 9628c2ecf20Sopenharmony_ci u8 local_port = mlxsw_sp_port->local_port; 9638c2ecf20Sopenharmony_ci u16 vid = mlxsw_sp_port_vlan->vid; 9648c2ecf20Sopenharmony_ci struct mlxsw_sp_fid *fid; 9658c2ecf20Sopenharmony_ci int err; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci bridge_device = bridge_port->bridge_device; 9688c2ecf20Sopenharmony_ci fid = bridge_device->ops->fid_get(bridge_device, vid, extack); 9698c2ecf20Sopenharmony_ci if (IS_ERR(fid)) 9708c2ecf20Sopenharmony_ci return PTR_ERR(fid); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci err = mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_UC, local_port, 9738c2ecf20Sopenharmony_ci bridge_port->flags & BR_FLOOD); 9748c2ecf20Sopenharmony_ci if (err) 9758c2ecf20Sopenharmony_ci goto err_fid_uc_flood_set; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci err = mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_MC, local_port, 9788c2ecf20Sopenharmony_ci mlxsw_sp_mc_flood(bridge_port)); 9798c2ecf20Sopenharmony_ci if (err) 9808c2ecf20Sopenharmony_ci goto err_fid_mc_flood_set; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci err = mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_BC, local_port, 9838c2ecf20Sopenharmony_ci true); 9848c2ecf20Sopenharmony_ci if (err) 9858c2ecf20Sopenharmony_ci goto err_fid_bc_flood_set; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid); 9888c2ecf20Sopenharmony_ci if (err) 9898c2ecf20Sopenharmony_ci goto err_fid_port_vid_map; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan->fid = fid; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci return 0; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cierr_fid_port_vid_map: 9968c2ecf20Sopenharmony_ci mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_BC, local_port, false); 9978c2ecf20Sopenharmony_cierr_fid_bc_flood_set: 9988c2ecf20Sopenharmony_ci mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_MC, local_port, false); 9998c2ecf20Sopenharmony_cierr_fid_mc_flood_set: 10008c2ecf20Sopenharmony_ci mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_UC, local_port, false); 10018c2ecf20Sopenharmony_cierr_fid_uc_flood_set: 10028c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 10038c2ecf20Sopenharmony_ci return err; 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic void 10078c2ecf20Sopenharmony_cimlxsw_sp_port_vlan_fid_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan) 10088c2ecf20Sopenharmony_ci{ 10098c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port; 10108c2ecf20Sopenharmony_ci struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid; 10118c2ecf20Sopenharmony_ci u8 local_port = mlxsw_sp_port->local_port; 10128c2ecf20Sopenharmony_ci u16 vid = mlxsw_sp_port_vlan->vid; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan->fid = NULL; 10158c2ecf20Sopenharmony_ci mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid); 10168c2ecf20Sopenharmony_ci mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_BC, local_port, false); 10178c2ecf20Sopenharmony_ci mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_MC, local_port, false); 10188c2ecf20Sopenharmony_ci mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_UC, local_port, false); 10198c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_cistatic u16 10238c2ecf20Sopenharmony_cimlxsw_sp_port_pvid_determine(const struct mlxsw_sp_port *mlxsw_sp_port, 10248c2ecf20Sopenharmony_ci u16 vid, bool is_pvid) 10258c2ecf20Sopenharmony_ci{ 10268c2ecf20Sopenharmony_ci if (is_pvid) 10278c2ecf20Sopenharmony_ci return vid; 10288c2ecf20Sopenharmony_ci else if (mlxsw_sp_port->pvid == vid) 10298c2ecf20Sopenharmony_ci return 0; /* Dis-allow untagged packets */ 10308c2ecf20Sopenharmony_ci else 10318c2ecf20Sopenharmony_ci return mlxsw_sp_port->pvid; 10328c2ecf20Sopenharmony_ci} 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_cistatic int 10358c2ecf20Sopenharmony_cimlxsw_sp_port_vlan_bridge_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan, 10368c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 10378c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port; 10408c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 10418c2ecf20Sopenharmony_ci u16 vid = mlxsw_sp_port_vlan->vid; 10428c2ecf20Sopenharmony_ci int err; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci /* No need to continue if only VLAN flags were changed */ 10458c2ecf20Sopenharmony_ci if (mlxsw_sp_port_vlan->bridge_port) 10468c2ecf20Sopenharmony_ci return 0; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci err = mlxsw_sp_port_vlan_fid_join(mlxsw_sp_port_vlan, bridge_port, 10498c2ecf20Sopenharmony_ci extack); 10508c2ecf20Sopenharmony_ci if (err) 10518c2ecf20Sopenharmony_ci return err; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, 10548c2ecf20Sopenharmony_ci bridge_port->flags & BR_LEARNING); 10558c2ecf20Sopenharmony_ci if (err) 10568c2ecf20Sopenharmony_ci goto err_port_vid_learning_set; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, 10598c2ecf20Sopenharmony_ci bridge_port->stp_state); 10608c2ecf20Sopenharmony_ci if (err) 10618c2ecf20Sopenharmony_ci goto err_port_vid_stp_set; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci bridge_vlan = mlxsw_sp_bridge_vlan_get(bridge_port, vid); 10648c2ecf20Sopenharmony_ci if (!bridge_vlan) { 10658c2ecf20Sopenharmony_ci err = -ENOMEM; 10668c2ecf20Sopenharmony_ci goto err_bridge_vlan_get; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci list_add(&mlxsw_sp_port_vlan->bridge_vlan_node, 10708c2ecf20Sopenharmony_ci &bridge_vlan->port_vlan_list); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci mlxsw_sp_bridge_port_get(mlxsw_sp_port->mlxsw_sp->bridge, 10738c2ecf20Sopenharmony_ci bridge_port->dev, extack); 10748c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan->bridge_port = bridge_port; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci return 0; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_cierr_bridge_vlan_get: 10798c2ecf20Sopenharmony_ci mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_DISABLED); 10808c2ecf20Sopenharmony_cierr_port_vid_stp_set: 10818c2ecf20Sopenharmony_ci mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false); 10828c2ecf20Sopenharmony_cierr_port_vid_learning_set: 10838c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan_fid_leave(mlxsw_sp_port_vlan); 10848c2ecf20Sopenharmony_ci return err; 10858c2ecf20Sopenharmony_ci} 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_civoid 10888c2ecf20Sopenharmony_cimlxsw_sp_port_vlan_bridge_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port; 10918c2ecf20Sopenharmony_ci struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid; 10928c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 10938c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 10948c2ecf20Sopenharmony_ci u16 vid = mlxsw_sp_port_vlan->vid; 10958c2ecf20Sopenharmony_ci bool last_port, last_vlan; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_8021Q && 10988c2ecf20Sopenharmony_ci mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_8021D)) 10998c2ecf20Sopenharmony_ci return; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_port_vlan->bridge_port; 11028c2ecf20Sopenharmony_ci last_vlan = list_is_singular(&bridge_port->vlans_list); 11038c2ecf20Sopenharmony_ci bridge_vlan = mlxsw_sp_bridge_vlan_find(bridge_port, vid); 11048c2ecf20Sopenharmony_ci last_port = list_is_singular(&bridge_vlan->port_vlan_list); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci list_del(&mlxsw_sp_port_vlan->bridge_vlan_node); 11078c2ecf20Sopenharmony_ci mlxsw_sp_bridge_vlan_put(bridge_vlan); 11088c2ecf20Sopenharmony_ci mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_DISABLED); 11098c2ecf20Sopenharmony_ci mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false); 11108c2ecf20Sopenharmony_ci if (last_port) 11118c2ecf20Sopenharmony_ci mlxsw_sp_bridge_port_fdb_flush(mlxsw_sp_port->mlxsw_sp, 11128c2ecf20Sopenharmony_ci bridge_port, 11138c2ecf20Sopenharmony_ci mlxsw_sp_fid_index(fid)); 11148c2ecf20Sopenharmony_ci if (last_vlan) 11158c2ecf20Sopenharmony_ci mlxsw_sp_bridge_port_mdb_flush(mlxsw_sp_port, bridge_port); 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan_fid_leave(mlxsw_sp_port_vlan); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci mlxsw_sp_bridge_port_put(mlxsw_sp_port->mlxsw_sp->bridge, bridge_port); 11208c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan->bridge_port = NULL; 11218c2ecf20Sopenharmony_ci} 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_cistatic int 11248c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port, 11258c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 11268c2ecf20Sopenharmony_ci u16 vid, bool is_untagged, bool is_pvid, 11278c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci u16 pvid = mlxsw_sp_port_pvid_determine(mlxsw_sp_port, vid, is_pvid); 11308c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 11318c2ecf20Sopenharmony_ci u16 old_pvid = mlxsw_sp_port->pvid; 11328c2ecf20Sopenharmony_ci int err; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci /* The only valid scenario in which a port-vlan already exists, is if 11358c2ecf20Sopenharmony_ci * the VLAN flags were changed and the port-vlan is associated with the 11368c2ecf20Sopenharmony_ci * correct bridge port 11378c2ecf20Sopenharmony_ci */ 11388c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid); 11398c2ecf20Sopenharmony_ci if (mlxsw_sp_port_vlan && 11408c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan->bridge_port != bridge_port) 11418c2ecf20Sopenharmony_ci return -EEXIST; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci if (!mlxsw_sp_port_vlan) { 11448c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_create(mlxsw_sp_port, 11458c2ecf20Sopenharmony_ci vid); 11468c2ecf20Sopenharmony_ci if (IS_ERR(mlxsw_sp_port_vlan)) 11478c2ecf20Sopenharmony_ci return PTR_ERR(mlxsw_sp_port_vlan); 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, 11518c2ecf20Sopenharmony_ci is_untagged); 11528c2ecf20Sopenharmony_ci if (err) 11538c2ecf20Sopenharmony_ci goto err_port_vlan_set; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, pvid); 11568c2ecf20Sopenharmony_ci if (err) 11578c2ecf20Sopenharmony_ci goto err_port_pvid_set; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci err = mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port, 11608c2ecf20Sopenharmony_ci extack); 11618c2ecf20Sopenharmony_ci if (err) 11628c2ecf20Sopenharmony_ci goto err_port_vlan_bridge_join; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci return 0; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cierr_port_vlan_bridge_join: 11678c2ecf20Sopenharmony_ci mlxsw_sp_port_pvid_set(mlxsw_sp_port, old_pvid); 11688c2ecf20Sopenharmony_cierr_port_pvid_set: 11698c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); 11708c2ecf20Sopenharmony_cierr_port_vlan_set: 11718c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan); 11728c2ecf20Sopenharmony_ci return err; 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_cistatic int 11768c2ecf20Sopenharmony_cimlxsw_sp_br_ban_rif_pvid_change(struct mlxsw_sp *mlxsw_sp, 11778c2ecf20Sopenharmony_ci const struct net_device *br_dev, 11788c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 11798c2ecf20Sopenharmony_ci{ 11808c2ecf20Sopenharmony_ci u16 pvid; 11818c2ecf20Sopenharmony_ci u16 vid; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci pvid = mlxsw_sp_rif_vid(mlxsw_sp, br_dev); 11848c2ecf20Sopenharmony_ci if (!pvid) 11858c2ecf20Sopenharmony_ci return 0; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { 11888c2ecf20Sopenharmony_ci if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { 11898c2ecf20Sopenharmony_ci if (vid != pvid) { 11908c2ecf20Sopenharmony_ci netdev_err(br_dev, "Can't change PVID, it's used by router interface\n"); 11918c2ecf20Sopenharmony_ci return -EBUSY; 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci } else { 11948c2ecf20Sopenharmony_ci if (vid == pvid) { 11958c2ecf20Sopenharmony_ci netdev_err(br_dev, "Can't remove PVID, it's used by router interface\n"); 11968c2ecf20Sopenharmony_ci return -EBUSY; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci return 0; 12028c2ecf20Sopenharmony_ci} 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, 12058c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan, 12068c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 12078c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 12108c2ecf20Sopenharmony_ci bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; 12118c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 12128c2ecf20Sopenharmony_ci struct net_device *orig_dev = vlan->obj.orig_dev; 12138c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 12148c2ecf20Sopenharmony_ci u16 vid; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci if (netif_is_bridge_master(orig_dev)) { 12178c2ecf20Sopenharmony_ci int err = 0; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci if ((vlan->flags & BRIDGE_VLAN_INFO_BRENTRY) && 12208c2ecf20Sopenharmony_ci br_vlan_enabled(orig_dev) && 12218c2ecf20Sopenharmony_ci switchdev_trans_ph_prepare(trans)) 12228c2ecf20Sopenharmony_ci err = mlxsw_sp_br_ban_rif_pvid_change(mlxsw_sp, 12238c2ecf20Sopenharmony_ci orig_dev, vlan); 12248c2ecf20Sopenharmony_ci if (!err) 12258c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 12268c2ecf20Sopenharmony_ci return err; 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci if (switchdev_trans_ph_commit(trans)) 12308c2ecf20Sopenharmony_ci return 0; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); 12338c2ecf20Sopenharmony_ci if (WARN_ON(!bridge_port)) 12348c2ecf20Sopenharmony_ci return -EINVAL; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci if (!bridge_port->bridge_device->vlan_enabled) 12378c2ecf20Sopenharmony_ci return 0; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 12408c2ecf20Sopenharmony_ci int err; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci err = mlxsw_sp_bridge_port_vlan_add(mlxsw_sp_port, bridge_port, 12438c2ecf20Sopenharmony_ci vid, flag_untagged, 12448c2ecf20Sopenharmony_ci flag_pvid, extack); 12458c2ecf20Sopenharmony_ci if (err) 12468c2ecf20Sopenharmony_ci return err; 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci return 0; 12508c2ecf20Sopenharmony_ci} 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_cistatic enum mlxsw_reg_sfdf_flush_type mlxsw_sp_fdb_flush_type(bool lagged) 12538c2ecf20Sopenharmony_ci{ 12548c2ecf20Sopenharmony_ci return lagged ? MLXSW_REG_SFDF_FLUSH_PER_LAG_AND_FID : 12558c2ecf20Sopenharmony_ci MLXSW_REG_SFDF_FLUSH_PER_PORT_AND_FID; 12568c2ecf20Sopenharmony_ci} 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_cistatic int 12598c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_fdb_flush(struct mlxsw_sp *mlxsw_sp, 12608c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 12618c2ecf20Sopenharmony_ci u16 fid_index) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci bool lagged = bridge_port->lagged; 12648c2ecf20Sopenharmony_ci char sfdf_pl[MLXSW_REG_SFDF_LEN]; 12658c2ecf20Sopenharmony_ci u16 system_port; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci system_port = lagged ? bridge_port->lag_id : bridge_port->system_port; 12688c2ecf20Sopenharmony_ci mlxsw_reg_sfdf_pack(sfdf_pl, mlxsw_sp_fdb_flush_type(lagged)); 12698c2ecf20Sopenharmony_ci mlxsw_reg_sfdf_fid_set(sfdf_pl, fid_index); 12708c2ecf20Sopenharmony_ci mlxsw_reg_sfdf_port_fid_system_port_set(sfdf_pl, system_port); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdf), sfdf_pl); 12738c2ecf20Sopenharmony_ci} 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_cistatic enum mlxsw_reg_sfd_rec_policy mlxsw_sp_sfd_rec_policy(bool dynamic) 12768c2ecf20Sopenharmony_ci{ 12778c2ecf20Sopenharmony_ci return dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS : 12788c2ecf20Sopenharmony_ci MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_MLAG; 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cistatic enum mlxsw_reg_sfd_op mlxsw_sp_sfd_op(bool adding) 12828c2ecf20Sopenharmony_ci{ 12838c2ecf20Sopenharmony_ci return adding ? MLXSW_REG_SFD_OP_WRITE_EDIT : 12848c2ecf20Sopenharmony_ci MLXSW_REG_SFD_OP_WRITE_REMOVE; 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_fdb_tunnel_uc_op(struct mlxsw_sp *mlxsw_sp, 12888c2ecf20Sopenharmony_ci const char *mac, u16 fid, 12898c2ecf20Sopenharmony_ci enum mlxsw_sp_l3proto proto, 12908c2ecf20Sopenharmony_ci const union mlxsw_sp_l3addr *addr, 12918c2ecf20Sopenharmony_ci bool adding, bool dynamic) 12928c2ecf20Sopenharmony_ci{ 12938c2ecf20Sopenharmony_ci enum mlxsw_reg_sfd_uc_tunnel_protocol sfd_proto; 12948c2ecf20Sopenharmony_ci char *sfd_pl; 12958c2ecf20Sopenharmony_ci u8 num_rec; 12968c2ecf20Sopenharmony_ci u32 uip; 12978c2ecf20Sopenharmony_ci int err; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci switch (proto) { 13008c2ecf20Sopenharmony_ci case MLXSW_SP_L3_PROTO_IPV4: 13018c2ecf20Sopenharmony_ci uip = be32_to_cpu(addr->addr4); 13028c2ecf20Sopenharmony_ci sfd_proto = MLXSW_REG_SFD_UC_TUNNEL_PROTOCOL_IPV4; 13038c2ecf20Sopenharmony_ci break; 13048c2ecf20Sopenharmony_ci case MLXSW_SP_L3_PROTO_IPV6: 13058c2ecf20Sopenharmony_ci default: 13068c2ecf20Sopenharmony_ci WARN_ON(1); 13078c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); 13118c2ecf20Sopenharmony_ci if (!sfd_pl) 13128c2ecf20Sopenharmony_ci return -ENOMEM; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); 13158c2ecf20Sopenharmony_ci mlxsw_reg_sfd_uc_tunnel_pack(sfd_pl, 0, 13168c2ecf20Sopenharmony_ci mlxsw_sp_sfd_rec_policy(dynamic), mac, fid, 13178c2ecf20Sopenharmony_ci MLXSW_REG_SFD_REC_ACTION_NOP, uip, 13188c2ecf20Sopenharmony_ci sfd_proto); 13198c2ecf20Sopenharmony_ci num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); 13208c2ecf20Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); 13218c2ecf20Sopenharmony_ci if (err) 13228c2ecf20Sopenharmony_ci goto out; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl)) 13258c2ecf20Sopenharmony_ci err = -EBUSY; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ciout: 13288c2ecf20Sopenharmony_ci kfree(sfd_pl); 13298c2ecf20Sopenharmony_ci return err; 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_cistatic int __mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u8 local_port, 13338c2ecf20Sopenharmony_ci const char *mac, u16 fid, bool adding, 13348c2ecf20Sopenharmony_ci enum mlxsw_reg_sfd_rec_action action, 13358c2ecf20Sopenharmony_ci enum mlxsw_reg_sfd_rec_policy policy) 13368c2ecf20Sopenharmony_ci{ 13378c2ecf20Sopenharmony_ci char *sfd_pl; 13388c2ecf20Sopenharmony_ci u8 num_rec; 13398c2ecf20Sopenharmony_ci int err; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); 13428c2ecf20Sopenharmony_ci if (!sfd_pl) 13438c2ecf20Sopenharmony_ci return -ENOMEM; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); 13468c2ecf20Sopenharmony_ci mlxsw_reg_sfd_uc_pack(sfd_pl, 0, policy, mac, fid, action, local_port); 13478c2ecf20Sopenharmony_ci num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); 13488c2ecf20Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); 13498c2ecf20Sopenharmony_ci if (err) 13508c2ecf20Sopenharmony_ci goto out; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl)) 13538c2ecf20Sopenharmony_ci err = -EBUSY; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ciout: 13568c2ecf20Sopenharmony_ci kfree(sfd_pl); 13578c2ecf20Sopenharmony_ci return err; 13588c2ecf20Sopenharmony_ci} 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u8 local_port, 13618c2ecf20Sopenharmony_ci const char *mac, u16 fid, bool adding, 13628c2ecf20Sopenharmony_ci bool dynamic) 13638c2ecf20Sopenharmony_ci{ 13648c2ecf20Sopenharmony_ci return __mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid, adding, 13658c2ecf20Sopenharmony_ci MLXSW_REG_SFD_REC_ACTION_NOP, 13668c2ecf20Sopenharmony_ci mlxsw_sp_sfd_rec_policy(dynamic)); 13678c2ecf20Sopenharmony_ci} 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ciint mlxsw_sp_rif_fdb_op(struct mlxsw_sp *mlxsw_sp, const char *mac, u16 fid, 13708c2ecf20Sopenharmony_ci bool adding) 13718c2ecf20Sopenharmony_ci{ 13728c2ecf20Sopenharmony_ci return __mlxsw_sp_port_fdb_uc_op(mlxsw_sp, 0, mac, fid, adding, 13738c2ecf20Sopenharmony_ci MLXSW_REG_SFD_REC_ACTION_FORWARD_IP_ROUTER, 13748c2ecf20Sopenharmony_ci MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY); 13758c2ecf20Sopenharmony_ci} 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id, 13788c2ecf20Sopenharmony_ci const char *mac, u16 fid, u16 lag_vid, 13798c2ecf20Sopenharmony_ci bool adding, bool dynamic) 13808c2ecf20Sopenharmony_ci{ 13818c2ecf20Sopenharmony_ci char *sfd_pl; 13828c2ecf20Sopenharmony_ci u8 num_rec; 13838c2ecf20Sopenharmony_ci int err; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); 13868c2ecf20Sopenharmony_ci if (!sfd_pl) 13878c2ecf20Sopenharmony_ci return -ENOMEM; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); 13908c2ecf20Sopenharmony_ci mlxsw_reg_sfd_uc_lag_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic), 13918c2ecf20Sopenharmony_ci mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP, 13928c2ecf20Sopenharmony_ci lag_vid, lag_id); 13938c2ecf20Sopenharmony_ci num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); 13948c2ecf20Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); 13958c2ecf20Sopenharmony_ci if (err) 13968c2ecf20Sopenharmony_ci goto out; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl)) 13998c2ecf20Sopenharmony_ci err = -EBUSY; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ciout: 14028c2ecf20Sopenharmony_ci kfree(sfd_pl); 14038c2ecf20Sopenharmony_ci return err; 14048c2ecf20Sopenharmony_ci} 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_cistatic int 14078c2ecf20Sopenharmony_cimlxsw_sp_port_fdb_set(struct mlxsw_sp_port *mlxsw_sp_port, 14088c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info, bool adding) 14098c2ecf20Sopenharmony_ci{ 14108c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 14118c2ecf20Sopenharmony_ci struct net_device *orig_dev = fdb_info->info.dev; 14128c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 14138c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 14148c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 14158c2ecf20Sopenharmony_ci u16 fid_index, vid; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); 14188c2ecf20Sopenharmony_ci if (!bridge_port) 14198c2ecf20Sopenharmony_ci return -EINVAL; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci bridge_device = bridge_port->bridge_device; 14228c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port, 14238c2ecf20Sopenharmony_ci bridge_device, 14248c2ecf20Sopenharmony_ci fdb_info->vid); 14258c2ecf20Sopenharmony_ci if (!mlxsw_sp_port_vlan) 14268c2ecf20Sopenharmony_ci return 0; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); 14298c2ecf20Sopenharmony_ci vid = mlxsw_sp_port_vlan->vid; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci if (!bridge_port->lagged) 14328c2ecf20Sopenharmony_ci return mlxsw_sp_port_fdb_uc_op(mlxsw_sp, 14338c2ecf20Sopenharmony_ci bridge_port->system_port, 14348c2ecf20Sopenharmony_ci fdb_info->addr, fid_index, 14358c2ecf20Sopenharmony_ci adding, false); 14368c2ecf20Sopenharmony_ci else 14378c2ecf20Sopenharmony_ci return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, 14388c2ecf20Sopenharmony_ci bridge_port->lag_id, 14398c2ecf20Sopenharmony_ci fdb_info->addr, fid_index, 14408c2ecf20Sopenharmony_ci vid, adding, false); 14418c2ecf20Sopenharmony_ci} 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr, 14448c2ecf20Sopenharmony_ci u16 fid, u16 mid_idx, bool adding) 14458c2ecf20Sopenharmony_ci{ 14468c2ecf20Sopenharmony_ci char *sfd_pl; 14478c2ecf20Sopenharmony_ci u8 num_rec; 14488c2ecf20Sopenharmony_ci int err; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); 14518c2ecf20Sopenharmony_ci if (!sfd_pl) 14528c2ecf20Sopenharmony_ci return -ENOMEM; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); 14558c2ecf20Sopenharmony_ci mlxsw_reg_sfd_mc_pack(sfd_pl, 0, addr, fid, 14568c2ecf20Sopenharmony_ci MLXSW_REG_SFD_REC_ACTION_NOP, mid_idx); 14578c2ecf20Sopenharmony_ci num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); 14588c2ecf20Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); 14598c2ecf20Sopenharmony_ci if (err) 14608c2ecf20Sopenharmony_ci goto out; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl)) 14638c2ecf20Sopenharmony_ci err = -EBUSY; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ciout: 14668c2ecf20Sopenharmony_ci kfree(sfd_pl); 14678c2ecf20Sopenharmony_ci return err; 14688c2ecf20Sopenharmony_ci} 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_smid_full_entry(struct mlxsw_sp *mlxsw_sp, u16 mid_idx, 14718c2ecf20Sopenharmony_ci long *ports_bitmap, 14728c2ecf20Sopenharmony_ci bool set_router_port) 14738c2ecf20Sopenharmony_ci{ 14748c2ecf20Sopenharmony_ci char *smid_pl; 14758c2ecf20Sopenharmony_ci int err, i; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci smid_pl = kmalloc(MLXSW_REG_SMID_LEN, GFP_KERNEL); 14788c2ecf20Sopenharmony_ci if (!smid_pl) 14798c2ecf20Sopenharmony_ci return -ENOMEM; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci mlxsw_reg_smid_pack(smid_pl, mid_idx, 0, false); 14828c2ecf20Sopenharmony_ci for (i = 1; i < mlxsw_core_max_ports(mlxsw_sp->core); i++) { 14838c2ecf20Sopenharmony_ci if (mlxsw_sp->ports[i]) 14848c2ecf20Sopenharmony_ci mlxsw_reg_smid_port_mask_set(smid_pl, i, 1); 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci mlxsw_reg_smid_port_mask_set(smid_pl, 14888c2ecf20Sopenharmony_ci mlxsw_sp_router_port(mlxsw_sp), 1); 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci for_each_set_bit(i, ports_bitmap, mlxsw_core_max_ports(mlxsw_sp->core)) 14918c2ecf20Sopenharmony_ci mlxsw_reg_smid_port_set(smid_pl, i, 1); 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci mlxsw_reg_smid_port_set(smid_pl, mlxsw_sp_router_port(mlxsw_sp), 14948c2ecf20Sopenharmony_ci set_router_port); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid), smid_pl); 14978c2ecf20Sopenharmony_ci kfree(smid_pl); 14988c2ecf20Sopenharmony_ci return err; 14998c2ecf20Sopenharmony_ci} 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_smid_set(struct mlxsw_sp_port *mlxsw_sp_port, 15028c2ecf20Sopenharmony_ci u16 mid_idx, bool add) 15038c2ecf20Sopenharmony_ci{ 15048c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 15058c2ecf20Sopenharmony_ci char *smid_pl; 15068c2ecf20Sopenharmony_ci int err; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci smid_pl = kmalloc(MLXSW_REG_SMID_LEN, GFP_KERNEL); 15098c2ecf20Sopenharmony_ci if (!smid_pl) 15108c2ecf20Sopenharmony_ci return -ENOMEM; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci mlxsw_reg_smid_pack(smid_pl, mid_idx, mlxsw_sp_port->local_port, add); 15138c2ecf20Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid), smid_pl); 15148c2ecf20Sopenharmony_ci kfree(smid_pl); 15158c2ecf20Sopenharmony_ci return err; 15168c2ecf20Sopenharmony_ci} 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_cistatic struct 15198c2ecf20Sopenharmony_cimlxsw_sp_mid *__mlxsw_sp_mc_get(struct mlxsw_sp_bridge_device *bridge_device, 15208c2ecf20Sopenharmony_ci const unsigned char *addr, 15218c2ecf20Sopenharmony_ci u16 fid) 15228c2ecf20Sopenharmony_ci{ 15238c2ecf20Sopenharmony_ci struct mlxsw_sp_mid *mid; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci list_for_each_entry(mid, &bridge_device->mids_list, list) { 15268c2ecf20Sopenharmony_ci if (ether_addr_equal(mid->addr, addr) && mid->fid == fid) 15278c2ecf20Sopenharmony_ci return mid; 15288c2ecf20Sopenharmony_ci } 15298c2ecf20Sopenharmony_ci return NULL; 15308c2ecf20Sopenharmony_ci} 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic void 15338c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_get_ports_bitmap(struct mlxsw_sp *mlxsw_sp, 15348c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 15358c2ecf20Sopenharmony_ci unsigned long *ports_bitmap) 15368c2ecf20Sopenharmony_ci{ 15378c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 15388c2ecf20Sopenharmony_ci u64 max_lag_members, i; 15398c2ecf20Sopenharmony_ci int lag_id; 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci if (!bridge_port->lagged) { 15428c2ecf20Sopenharmony_ci set_bit(bridge_port->system_port, ports_bitmap); 15438c2ecf20Sopenharmony_ci } else { 15448c2ecf20Sopenharmony_ci max_lag_members = MLXSW_CORE_RES_GET(mlxsw_sp->core, 15458c2ecf20Sopenharmony_ci MAX_LAG_MEMBERS); 15468c2ecf20Sopenharmony_ci lag_id = bridge_port->lag_id; 15478c2ecf20Sopenharmony_ci for (i = 0; i < max_lag_members; i++) { 15488c2ecf20Sopenharmony_ci mlxsw_sp_port = mlxsw_sp_port_lagged_get(mlxsw_sp, 15498c2ecf20Sopenharmony_ci lag_id, i); 15508c2ecf20Sopenharmony_ci if (mlxsw_sp_port) 15518c2ecf20Sopenharmony_ci set_bit(mlxsw_sp_port->local_port, 15528c2ecf20Sopenharmony_ci ports_bitmap); 15538c2ecf20Sopenharmony_ci } 15548c2ecf20Sopenharmony_ci } 15558c2ecf20Sopenharmony_ci} 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_cistatic void 15588c2ecf20Sopenharmony_cimlxsw_sp_mc_get_mrouters_bitmap(unsigned long *flood_bitmap, 15598c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 15608c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp) 15618c2ecf20Sopenharmony_ci{ 15628c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci list_for_each_entry(bridge_port, &bridge_device->ports_list, list) { 15658c2ecf20Sopenharmony_ci if (bridge_port->mrouter) { 15668c2ecf20Sopenharmony_ci mlxsw_sp_bridge_port_get_ports_bitmap(mlxsw_sp, 15678c2ecf20Sopenharmony_ci bridge_port, 15688c2ecf20Sopenharmony_ci flood_bitmap); 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ci} 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_cistatic bool 15748c2ecf20Sopenharmony_cimlxsw_sp_mc_write_mdb_entry(struct mlxsw_sp *mlxsw_sp, 15758c2ecf20Sopenharmony_ci struct mlxsw_sp_mid *mid, 15768c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci long *flood_bitmap; 15798c2ecf20Sopenharmony_ci int num_of_ports; 15808c2ecf20Sopenharmony_ci int alloc_size; 15818c2ecf20Sopenharmony_ci u16 mid_idx; 15828c2ecf20Sopenharmony_ci int err; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci mid_idx = find_first_zero_bit(mlxsw_sp->bridge->mids_bitmap, 15858c2ecf20Sopenharmony_ci MLXSW_SP_MID_MAX); 15868c2ecf20Sopenharmony_ci if (mid_idx == MLXSW_SP_MID_MAX) 15878c2ecf20Sopenharmony_ci return false; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci num_of_ports = mlxsw_core_max_ports(mlxsw_sp->core); 15908c2ecf20Sopenharmony_ci alloc_size = sizeof(long) * BITS_TO_LONGS(num_of_ports); 15918c2ecf20Sopenharmony_ci flood_bitmap = kzalloc(alloc_size, GFP_KERNEL); 15928c2ecf20Sopenharmony_ci if (!flood_bitmap) 15938c2ecf20Sopenharmony_ci return false; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci bitmap_copy(flood_bitmap, mid->ports_in_mid, num_of_ports); 15968c2ecf20Sopenharmony_ci mlxsw_sp_mc_get_mrouters_bitmap(flood_bitmap, bridge_device, mlxsw_sp); 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci mid->mid = mid_idx; 15998c2ecf20Sopenharmony_ci err = mlxsw_sp_port_smid_full_entry(mlxsw_sp, mid_idx, flood_bitmap, 16008c2ecf20Sopenharmony_ci bridge_device->mrouter); 16018c2ecf20Sopenharmony_ci kfree(flood_bitmap); 16028c2ecf20Sopenharmony_ci if (err) 16038c2ecf20Sopenharmony_ci return false; 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci err = mlxsw_sp_port_mdb_op(mlxsw_sp, mid->addr, mid->fid, mid_idx, 16068c2ecf20Sopenharmony_ci true); 16078c2ecf20Sopenharmony_ci if (err) 16088c2ecf20Sopenharmony_ci return false; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci set_bit(mid_idx, mlxsw_sp->bridge->mids_bitmap); 16118c2ecf20Sopenharmony_ci mid->in_hw = true; 16128c2ecf20Sopenharmony_ci return true; 16138c2ecf20Sopenharmony_ci} 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_cistatic int mlxsw_sp_mc_remove_mdb_entry(struct mlxsw_sp *mlxsw_sp, 16168c2ecf20Sopenharmony_ci struct mlxsw_sp_mid *mid) 16178c2ecf20Sopenharmony_ci{ 16188c2ecf20Sopenharmony_ci if (!mid->in_hw) 16198c2ecf20Sopenharmony_ci return 0; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci clear_bit(mid->mid, mlxsw_sp->bridge->mids_bitmap); 16228c2ecf20Sopenharmony_ci mid->in_hw = false; 16238c2ecf20Sopenharmony_ci return mlxsw_sp_port_mdb_op(mlxsw_sp, mid->addr, mid->fid, mid->mid, 16248c2ecf20Sopenharmony_ci false); 16258c2ecf20Sopenharmony_ci} 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_cistatic struct 16288c2ecf20Sopenharmony_cimlxsw_sp_mid *__mlxsw_sp_mc_alloc(struct mlxsw_sp *mlxsw_sp, 16298c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 16308c2ecf20Sopenharmony_ci const unsigned char *addr, 16318c2ecf20Sopenharmony_ci u16 fid) 16328c2ecf20Sopenharmony_ci{ 16338c2ecf20Sopenharmony_ci struct mlxsw_sp_mid *mid; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci mid = kzalloc(sizeof(*mid), GFP_KERNEL); 16368c2ecf20Sopenharmony_ci if (!mid) 16378c2ecf20Sopenharmony_ci return NULL; 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci mid->ports_in_mid = bitmap_zalloc(mlxsw_core_max_ports(mlxsw_sp->core), 16408c2ecf20Sopenharmony_ci GFP_KERNEL); 16418c2ecf20Sopenharmony_ci if (!mid->ports_in_mid) 16428c2ecf20Sopenharmony_ci goto err_ports_in_mid_alloc; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci ether_addr_copy(mid->addr, addr); 16458c2ecf20Sopenharmony_ci mid->fid = fid; 16468c2ecf20Sopenharmony_ci mid->in_hw = false; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci if (!bridge_device->multicast_enabled) 16498c2ecf20Sopenharmony_ci goto out; 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci if (!mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mid, bridge_device)) 16528c2ecf20Sopenharmony_ci goto err_write_mdb_entry; 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ciout: 16558c2ecf20Sopenharmony_ci list_add_tail(&mid->list, &bridge_device->mids_list); 16568c2ecf20Sopenharmony_ci return mid; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_cierr_write_mdb_entry: 16598c2ecf20Sopenharmony_ci bitmap_free(mid->ports_in_mid); 16608c2ecf20Sopenharmony_cierr_ports_in_mid_alloc: 16618c2ecf20Sopenharmony_ci kfree(mid); 16628c2ecf20Sopenharmony_ci return NULL; 16638c2ecf20Sopenharmony_ci} 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_remove_from_mid(struct mlxsw_sp_port *mlxsw_sp_port, 16668c2ecf20Sopenharmony_ci struct mlxsw_sp_mid *mid) 16678c2ecf20Sopenharmony_ci{ 16688c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 16698c2ecf20Sopenharmony_ci int err = 0; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci clear_bit(mlxsw_sp_port->local_port, mid->ports_in_mid); 16728c2ecf20Sopenharmony_ci if (bitmap_empty(mid->ports_in_mid, 16738c2ecf20Sopenharmony_ci mlxsw_core_max_ports(mlxsw_sp->core))) { 16748c2ecf20Sopenharmony_ci err = mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mid); 16758c2ecf20Sopenharmony_ci list_del(&mid->list); 16768c2ecf20Sopenharmony_ci bitmap_free(mid->ports_in_mid); 16778c2ecf20Sopenharmony_ci kfree(mid); 16788c2ecf20Sopenharmony_ci } 16798c2ecf20Sopenharmony_ci return err; 16808c2ecf20Sopenharmony_ci} 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port, 16838c2ecf20Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb, 16848c2ecf20Sopenharmony_ci struct switchdev_trans *trans) 16858c2ecf20Sopenharmony_ci{ 16868c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 16878c2ecf20Sopenharmony_ci struct net_device *orig_dev = mdb->obj.orig_dev; 16888c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 16898c2ecf20Sopenharmony_ci struct net_device *dev = mlxsw_sp_port->dev; 16908c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 16918c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 16928c2ecf20Sopenharmony_ci struct mlxsw_sp_mid *mid; 16938c2ecf20Sopenharmony_ci u16 fid_index; 16948c2ecf20Sopenharmony_ci int err = 0; 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci if (switchdev_trans_ph_commit(trans)) 16978c2ecf20Sopenharmony_ci return 0; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); 17008c2ecf20Sopenharmony_ci if (!bridge_port) 17018c2ecf20Sopenharmony_ci return 0; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci bridge_device = bridge_port->bridge_device; 17048c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port, 17058c2ecf20Sopenharmony_ci bridge_device, 17068c2ecf20Sopenharmony_ci mdb->vid); 17078c2ecf20Sopenharmony_ci if (!mlxsw_sp_port_vlan) 17088c2ecf20Sopenharmony_ci return 0; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci mid = __mlxsw_sp_mc_get(bridge_device, mdb->addr, fid_index); 17138c2ecf20Sopenharmony_ci if (!mid) { 17148c2ecf20Sopenharmony_ci mid = __mlxsw_sp_mc_alloc(mlxsw_sp, bridge_device, mdb->addr, 17158c2ecf20Sopenharmony_ci fid_index); 17168c2ecf20Sopenharmony_ci if (!mid) { 17178c2ecf20Sopenharmony_ci netdev_err(dev, "Unable to allocate MC group\n"); 17188c2ecf20Sopenharmony_ci return -ENOMEM; 17198c2ecf20Sopenharmony_ci } 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci set_bit(mlxsw_sp_port->local_port, mid->ports_in_mid); 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci if (!bridge_device->multicast_enabled) 17248c2ecf20Sopenharmony_ci return 0; 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci if (bridge_port->mrouter) 17278c2ecf20Sopenharmony_ci return 0; 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, true); 17308c2ecf20Sopenharmony_ci if (err) { 17318c2ecf20Sopenharmony_ci netdev_err(dev, "Unable to set SMID\n"); 17328c2ecf20Sopenharmony_ci goto err_out; 17338c2ecf20Sopenharmony_ci } 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci return 0; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_cierr_out: 17388c2ecf20Sopenharmony_ci mlxsw_sp_port_remove_from_mid(mlxsw_sp_port, mid); 17398c2ecf20Sopenharmony_ci return err; 17408c2ecf20Sopenharmony_ci} 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_cistatic void 17438c2ecf20Sopenharmony_cimlxsw_sp_bridge_mdb_mc_enable_sync(struct mlxsw_sp_port *mlxsw_sp_port, 17448c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device 17458c2ecf20Sopenharmony_ci *bridge_device) 17468c2ecf20Sopenharmony_ci{ 17478c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 17488c2ecf20Sopenharmony_ci struct mlxsw_sp_mid *mid; 17498c2ecf20Sopenharmony_ci bool mc_enabled; 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci mc_enabled = bridge_device->multicast_enabled; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci list_for_each_entry(mid, &bridge_device->mids_list, list) { 17548c2ecf20Sopenharmony_ci if (mc_enabled) 17558c2ecf20Sopenharmony_ci mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mid, 17568c2ecf20Sopenharmony_ci bridge_device); 17578c2ecf20Sopenharmony_ci else 17588c2ecf20Sopenharmony_ci mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mid); 17598c2ecf20Sopenharmony_ci } 17608c2ecf20Sopenharmony_ci} 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_cistatic void 17638c2ecf20Sopenharmony_cimlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port, 17648c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 17658c2ecf20Sopenharmony_ci bool add) 17668c2ecf20Sopenharmony_ci{ 17678c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 17688c2ecf20Sopenharmony_ci struct mlxsw_sp_mid *mid; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci bridge_device = bridge_port->bridge_device; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci list_for_each_entry(mid, &bridge_device->mids_list, list) { 17738c2ecf20Sopenharmony_ci if (!test_bit(mlxsw_sp_port->local_port, mid->ports_in_mid)) 17748c2ecf20Sopenharmony_ci mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, add); 17758c2ecf20Sopenharmony_ci } 17768c2ecf20Sopenharmony_ci} 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_obj_add(struct net_device *dev, 17798c2ecf20Sopenharmony_ci const struct switchdev_obj *obj, 17808c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 17818c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 17828c2ecf20Sopenharmony_ci{ 17838c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 17848c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan; 17858c2ecf20Sopenharmony_ci int err = 0; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci switch (obj->id) { 17888c2ecf20Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 17898c2ecf20Sopenharmony_ci vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); 17908c2ecf20Sopenharmony_ci err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, trans, 17918c2ecf20Sopenharmony_ci extack); 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) { 17948c2ecf20Sopenharmony_ci /* The event is emitted before the changes are actually 17958c2ecf20Sopenharmony_ci * applied to the bridge. Therefore schedule the respin 17968c2ecf20Sopenharmony_ci * call for later, so that the respin logic sees the 17978c2ecf20Sopenharmony_ci * updated bridge state. 17988c2ecf20Sopenharmony_ci */ 17998c2ecf20Sopenharmony_ci mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp); 18008c2ecf20Sopenharmony_ci } 18018c2ecf20Sopenharmony_ci break; 18028c2ecf20Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_MDB: 18038c2ecf20Sopenharmony_ci err = mlxsw_sp_port_mdb_add(mlxsw_sp_port, 18048c2ecf20Sopenharmony_ci SWITCHDEV_OBJ_PORT_MDB(obj), 18058c2ecf20Sopenharmony_ci trans); 18068c2ecf20Sopenharmony_ci break; 18078c2ecf20Sopenharmony_ci default: 18088c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 18098c2ecf20Sopenharmony_ci break; 18108c2ecf20Sopenharmony_ci } 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci return err; 18138c2ecf20Sopenharmony_ci} 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_cistatic void 18168c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_vlan_del(struct mlxsw_sp_port *mlxsw_sp_port, 18178c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, u16 vid) 18188c2ecf20Sopenharmony_ci{ 18198c2ecf20Sopenharmony_ci u16 pvid = mlxsw_sp_port->pvid == vid ? 0 : mlxsw_sp_port->pvid; 18208c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid); 18238c2ecf20Sopenharmony_ci if (WARN_ON(!mlxsw_sp_port_vlan)) 18248c2ecf20Sopenharmony_ci return; 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan); 18278c2ecf20Sopenharmony_ci mlxsw_sp_port_pvid_set(mlxsw_sp_port, pvid); 18288c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); 18298c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan); 18308c2ecf20Sopenharmony_ci} 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, 18338c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 18348c2ecf20Sopenharmony_ci{ 18358c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 18368c2ecf20Sopenharmony_ci struct net_device *orig_dev = vlan->obj.orig_dev; 18378c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 18388c2ecf20Sopenharmony_ci u16 vid; 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci if (netif_is_bridge_master(orig_dev)) 18418c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); 18448c2ecf20Sopenharmony_ci if (WARN_ON(!bridge_port)) 18458c2ecf20Sopenharmony_ci return -EINVAL; 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci if (!bridge_port->bridge_device->vlan_enabled) 18488c2ecf20Sopenharmony_ci return 0; 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) 18518c2ecf20Sopenharmony_ci mlxsw_sp_bridge_port_vlan_del(mlxsw_sp_port, bridge_port, vid); 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci return 0; 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_cistatic int 18578c2ecf20Sopenharmony_ci__mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port, 18588c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 18598c2ecf20Sopenharmony_ci struct mlxsw_sp_mid *mid) 18608c2ecf20Sopenharmony_ci{ 18618c2ecf20Sopenharmony_ci struct net_device *dev = mlxsw_sp_port->dev; 18628c2ecf20Sopenharmony_ci int err; 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci if (bridge_port->bridge_device->multicast_enabled && 18658c2ecf20Sopenharmony_ci !bridge_port->mrouter) { 18668c2ecf20Sopenharmony_ci err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, false); 18678c2ecf20Sopenharmony_ci if (err) 18688c2ecf20Sopenharmony_ci netdev_err(dev, "Unable to remove port from SMID\n"); 18698c2ecf20Sopenharmony_ci } 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci err = mlxsw_sp_port_remove_from_mid(mlxsw_sp_port, mid); 18728c2ecf20Sopenharmony_ci if (err) 18738c2ecf20Sopenharmony_ci netdev_err(dev, "Unable to remove MC SFD\n"); 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci return err; 18768c2ecf20Sopenharmony_ci} 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port, 18798c2ecf20Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb) 18808c2ecf20Sopenharmony_ci{ 18818c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 18828c2ecf20Sopenharmony_ci struct net_device *orig_dev = mdb->obj.orig_dev; 18838c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 18848c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 18858c2ecf20Sopenharmony_ci struct net_device *dev = mlxsw_sp_port->dev; 18868c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 18878c2ecf20Sopenharmony_ci struct mlxsw_sp_mid *mid; 18888c2ecf20Sopenharmony_ci u16 fid_index; 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); 18918c2ecf20Sopenharmony_ci if (!bridge_port) 18928c2ecf20Sopenharmony_ci return 0; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci bridge_device = bridge_port->bridge_device; 18958c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port, 18968c2ecf20Sopenharmony_ci bridge_device, 18978c2ecf20Sopenharmony_ci mdb->vid); 18988c2ecf20Sopenharmony_ci if (!mlxsw_sp_port_vlan) 18998c2ecf20Sopenharmony_ci return 0; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci mid = __mlxsw_sp_mc_get(bridge_device, mdb->addr, fid_index); 19048c2ecf20Sopenharmony_ci if (!mid) { 19058c2ecf20Sopenharmony_ci netdev_err(dev, "Unable to remove port from MC DB\n"); 19068c2ecf20Sopenharmony_ci return -EINVAL; 19078c2ecf20Sopenharmony_ci } 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci return __mlxsw_sp_port_mdb_del(mlxsw_sp_port, bridge_port, mid); 19108c2ecf20Sopenharmony_ci} 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_cistatic void 19138c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, 19148c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port) 19158c2ecf20Sopenharmony_ci{ 19168c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 19178c2ecf20Sopenharmony_ci struct mlxsw_sp_mid *mid, *tmp; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci bridge_device = bridge_port->bridge_device; 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci list_for_each_entry_safe(mid, tmp, &bridge_device->mids_list, list) { 19228c2ecf20Sopenharmony_ci if (test_bit(mlxsw_sp_port->local_port, mid->ports_in_mid)) { 19238c2ecf20Sopenharmony_ci __mlxsw_sp_port_mdb_del(mlxsw_sp_port, bridge_port, 19248c2ecf20Sopenharmony_ci mid); 19258c2ecf20Sopenharmony_ci } else if (bridge_device->multicast_enabled && 19268c2ecf20Sopenharmony_ci bridge_port->mrouter) { 19278c2ecf20Sopenharmony_ci mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, false); 19288c2ecf20Sopenharmony_ci } 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci} 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_cistatic int mlxsw_sp_port_obj_del(struct net_device *dev, 19338c2ecf20Sopenharmony_ci const struct switchdev_obj *obj) 19348c2ecf20Sopenharmony_ci{ 19358c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 19368c2ecf20Sopenharmony_ci int err = 0; 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci switch (obj->id) { 19398c2ecf20Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 19408c2ecf20Sopenharmony_ci err = mlxsw_sp_port_vlans_del(mlxsw_sp_port, 19418c2ecf20Sopenharmony_ci SWITCHDEV_OBJ_PORT_VLAN(obj)); 19428c2ecf20Sopenharmony_ci break; 19438c2ecf20Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_MDB: 19448c2ecf20Sopenharmony_ci err = mlxsw_sp_port_mdb_del(mlxsw_sp_port, 19458c2ecf20Sopenharmony_ci SWITCHDEV_OBJ_PORT_MDB(obj)); 19468c2ecf20Sopenharmony_ci break; 19478c2ecf20Sopenharmony_ci default: 19488c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 19498c2ecf20Sopenharmony_ci break; 19508c2ecf20Sopenharmony_ci } 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp); 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci return err; 19558c2ecf20Sopenharmony_ci} 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_cistatic struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp, 19588c2ecf20Sopenharmony_ci u16 lag_id) 19598c2ecf20Sopenharmony_ci{ 19608c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 19618c2ecf20Sopenharmony_ci u64 max_lag_members; 19628c2ecf20Sopenharmony_ci int i; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci max_lag_members = MLXSW_CORE_RES_GET(mlxsw_sp->core, 19658c2ecf20Sopenharmony_ci MAX_LAG_MEMBERS); 19668c2ecf20Sopenharmony_ci for (i = 0; i < max_lag_members; i++) { 19678c2ecf20Sopenharmony_ci mlxsw_sp_port = mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i); 19688c2ecf20Sopenharmony_ci if (mlxsw_sp_port) 19698c2ecf20Sopenharmony_ci return mlxsw_sp_port; 19708c2ecf20Sopenharmony_ci } 19718c2ecf20Sopenharmony_ci return NULL; 19728c2ecf20Sopenharmony_ci} 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_cistatic int 19758c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021q_port_join(struct mlxsw_sp_bridge_device *bridge_device, 19768c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 19778c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 19788c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 19798c2ecf20Sopenharmony_ci{ 19808c2ecf20Sopenharmony_ci if (is_vlan_dev(bridge_port->dev)) { 19818c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Can not enslave a VLAN device to a VLAN-aware bridge"); 19828c2ecf20Sopenharmony_ci return -EINVAL; 19838c2ecf20Sopenharmony_ci } 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci /* Port is no longer usable as a router interface */ 19868c2ecf20Sopenharmony_ci if (mlxsw_sp_port->default_vlan->fid) 19878c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port->default_vlan); 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci return 0; 19908c2ecf20Sopenharmony_ci} 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_cistatic void 19938c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021q_port_leave(struct mlxsw_sp_bridge_device *bridge_device, 19948c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 19958c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port) 19968c2ecf20Sopenharmony_ci{ 19978c2ecf20Sopenharmony_ci /* Make sure untagged frames are allowed to ingress */ 19988c2ecf20Sopenharmony_ci mlxsw_sp_port_pvid_set(mlxsw_sp_port, MLXSW_SP_DEFAULT_VID); 19998c2ecf20Sopenharmony_ci} 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_cistatic int 20028c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021q_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device, 20038c2ecf20Sopenharmony_ci const struct net_device *vxlan_dev, u16 vid, 20048c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 20058c2ecf20Sopenharmony_ci{ 20068c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); 20078c2ecf20Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); 20088c2ecf20Sopenharmony_ci struct mlxsw_sp_nve_params params = { 20098c2ecf20Sopenharmony_ci .type = MLXSW_SP_NVE_TYPE_VXLAN, 20108c2ecf20Sopenharmony_ci .vni = vxlan->cfg.vni, 20118c2ecf20Sopenharmony_ci .dev = vxlan_dev, 20128c2ecf20Sopenharmony_ci }; 20138c2ecf20Sopenharmony_ci struct mlxsw_sp_fid *fid; 20148c2ecf20Sopenharmony_ci int err; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci /* If the VLAN is 0, we need to find the VLAN that is configured as 20178c2ecf20Sopenharmony_ci * PVID and egress untagged on the bridge port of the VxLAN device. 20188c2ecf20Sopenharmony_ci * It is possible no such VLAN exists 20198c2ecf20Sopenharmony_ci */ 20208c2ecf20Sopenharmony_ci if (!vid) { 20218c2ecf20Sopenharmony_ci err = mlxsw_sp_vxlan_mapped_vid(vxlan_dev, &vid); 20228c2ecf20Sopenharmony_ci if (err || !vid) 20238c2ecf20Sopenharmony_ci return err; 20248c2ecf20Sopenharmony_ci } 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci fid = mlxsw_sp_fid_8021q_get(mlxsw_sp, vid); 20278c2ecf20Sopenharmony_ci if (IS_ERR(fid)) { 20288c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1Q FID"); 20298c2ecf20Sopenharmony_ci return PTR_ERR(fid); 20308c2ecf20Sopenharmony_ci } 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci if (mlxsw_sp_fid_vni_is_set(fid)) { 20338c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VNI is already set on FID"); 20348c2ecf20Sopenharmony_ci err = -EINVAL; 20358c2ecf20Sopenharmony_ci goto err_vni_exists; 20368c2ecf20Sopenharmony_ci } 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci err = mlxsw_sp_nve_fid_enable(mlxsw_sp, fid, ¶ms, extack); 20398c2ecf20Sopenharmony_ci if (err) 20408c2ecf20Sopenharmony_ci goto err_nve_fid_enable; 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci return 0; 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_cierr_nve_fid_enable: 20458c2ecf20Sopenharmony_cierr_vni_exists: 20468c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 20478c2ecf20Sopenharmony_ci return err; 20488c2ecf20Sopenharmony_ci} 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_cistatic struct net_device * 20518c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021q_vxlan_dev_find(struct net_device *br_dev, u16 vid) 20528c2ecf20Sopenharmony_ci{ 20538c2ecf20Sopenharmony_ci struct net_device *dev; 20548c2ecf20Sopenharmony_ci struct list_head *iter; 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci netdev_for_each_lower_dev(br_dev, dev, iter) { 20578c2ecf20Sopenharmony_ci u16 pvid; 20588c2ecf20Sopenharmony_ci int err; 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci if (!netif_is_vxlan(dev)) 20618c2ecf20Sopenharmony_ci continue; 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci err = mlxsw_sp_vxlan_mapped_vid(dev, &pvid); 20648c2ecf20Sopenharmony_ci if (err || pvid != vid) 20658c2ecf20Sopenharmony_ci continue; 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci return dev; 20688c2ecf20Sopenharmony_ci } 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci return NULL; 20718c2ecf20Sopenharmony_ci} 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fid * 20748c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021q_fid_get(struct mlxsw_sp_bridge_device *bridge_device, 20758c2ecf20Sopenharmony_ci u16 vid, struct netlink_ext_ack *extack) 20768c2ecf20Sopenharmony_ci{ 20778c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci return mlxsw_sp_fid_8021q_get(mlxsw_sp, vid); 20808c2ecf20Sopenharmony_ci} 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fid * 20838c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021q_fid_lookup(struct mlxsw_sp_bridge_device *bridge_device, 20848c2ecf20Sopenharmony_ci u16 vid) 20858c2ecf20Sopenharmony_ci{ 20868c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci return mlxsw_sp_fid_8021q_lookup(mlxsw_sp, vid); 20898c2ecf20Sopenharmony_ci} 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_cistatic u16 20928c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021q_fid_vid(struct mlxsw_sp_bridge_device *bridge_device, 20938c2ecf20Sopenharmony_ci const struct mlxsw_sp_fid *fid) 20948c2ecf20Sopenharmony_ci{ 20958c2ecf20Sopenharmony_ci return mlxsw_sp_fid_8021q_vid(fid); 20968c2ecf20Sopenharmony_ci} 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021q_ops = { 20998c2ecf20Sopenharmony_ci .port_join = mlxsw_sp_bridge_8021q_port_join, 21008c2ecf20Sopenharmony_ci .port_leave = mlxsw_sp_bridge_8021q_port_leave, 21018c2ecf20Sopenharmony_ci .vxlan_join = mlxsw_sp_bridge_8021q_vxlan_join, 21028c2ecf20Sopenharmony_ci .fid_get = mlxsw_sp_bridge_8021q_fid_get, 21038c2ecf20Sopenharmony_ci .fid_lookup = mlxsw_sp_bridge_8021q_fid_lookup, 21048c2ecf20Sopenharmony_ci .fid_vid = mlxsw_sp_bridge_8021q_fid_vid, 21058c2ecf20Sopenharmony_ci}; 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_cistatic bool 21088c2ecf20Sopenharmony_cimlxsw_sp_port_is_br_member(const struct mlxsw_sp_port *mlxsw_sp_port, 21098c2ecf20Sopenharmony_ci const struct net_device *br_dev) 21108c2ecf20Sopenharmony_ci{ 21118c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list, 21148c2ecf20Sopenharmony_ci list) { 21158c2ecf20Sopenharmony_ci if (mlxsw_sp_port_vlan->bridge_port && 21168c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan->bridge_port->bridge_device->dev == 21178c2ecf20Sopenharmony_ci br_dev) 21188c2ecf20Sopenharmony_ci return true; 21198c2ecf20Sopenharmony_ci } 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci return false; 21228c2ecf20Sopenharmony_ci} 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_cistatic int 21258c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021d_port_join(struct mlxsw_sp_bridge_device *bridge_device, 21268c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 21278c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 21288c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 21298c2ecf20Sopenharmony_ci{ 21308c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 21318c2ecf20Sopenharmony_ci struct net_device *dev = bridge_port->dev; 21328c2ecf20Sopenharmony_ci u16 vid; 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : MLXSW_SP_DEFAULT_VID; 21358c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid); 21368c2ecf20Sopenharmony_ci if (WARN_ON(!mlxsw_sp_port_vlan)) 21378c2ecf20Sopenharmony_ci return -EINVAL; 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci if (mlxsw_sp_port_is_br_member(mlxsw_sp_port, bridge_device->dev)) { 21408c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Can not bridge VLAN uppers of the same port"); 21418c2ecf20Sopenharmony_ci return -EINVAL; 21428c2ecf20Sopenharmony_ci } 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci /* Port is no longer usable as a router interface */ 21458c2ecf20Sopenharmony_ci if (mlxsw_sp_port_vlan->fid) 21468c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan); 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci return mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port, 21498c2ecf20Sopenharmony_ci extack); 21508c2ecf20Sopenharmony_ci} 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_cistatic void 21538c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021d_port_leave(struct mlxsw_sp_bridge_device *bridge_device, 21548c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 21558c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port) 21568c2ecf20Sopenharmony_ci{ 21578c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 21588c2ecf20Sopenharmony_ci struct net_device *dev = bridge_port->dev; 21598c2ecf20Sopenharmony_ci u16 vid; 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : MLXSW_SP_DEFAULT_VID; 21628c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid); 21638c2ecf20Sopenharmony_ci if (!mlxsw_sp_port_vlan || !mlxsw_sp_port_vlan->bridge_port) 21648c2ecf20Sopenharmony_ci return; 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan); 21678c2ecf20Sopenharmony_ci} 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_cistatic int 21708c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device, 21718c2ecf20Sopenharmony_ci const struct net_device *vxlan_dev, u16 vid, 21728c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 21738c2ecf20Sopenharmony_ci{ 21748c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); 21758c2ecf20Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); 21768c2ecf20Sopenharmony_ci struct mlxsw_sp_nve_params params = { 21778c2ecf20Sopenharmony_ci .type = MLXSW_SP_NVE_TYPE_VXLAN, 21788c2ecf20Sopenharmony_ci .vni = vxlan->cfg.vni, 21798c2ecf20Sopenharmony_ci .dev = vxlan_dev, 21808c2ecf20Sopenharmony_ci }; 21818c2ecf20Sopenharmony_ci struct mlxsw_sp_fid *fid; 21828c2ecf20Sopenharmony_ci int err; 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci fid = mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex); 21858c2ecf20Sopenharmony_ci if (IS_ERR(fid)) { 21868c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1D FID"); 21878c2ecf20Sopenharmony_ci return -EINVAL; 21888c2ecf20Sopenharmony_ci } 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci if (mlxsw_sp_fid_vni_is_set(fid)) { 21918c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VNI is already set on FID"); 21928c2ecf20Sopenharmony_ci err = -EINVAL; 21938c2ecf20Sopenharmony_ci goto err_vni_exists; 21948c2ecf20Sopenharmony_ci } 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci err = mlxsw_sp_nve_fid_enable(mlxsw_sp, fid, ¶ms, extack); 21978c2ecf20Sopenharmony_ci if (err) 21988c2ecf20Sopenharmony_ci goto err_nve_fid_enable; 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci return 0; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_cierr_nve_fid_enable: 22038c2ecf20Sopenharmony_cierr_vni_exists: 22048c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 22058c2ecf20Sopenharmony_ci return err; 22068c2ecf20Sopenharmony_ci} 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fid * 22098c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021d_fid_get(struct mlxsw_sp_bridge_device *bridge_device, 22108c2ecf20Sopenharmony_ci u16 vid, struct netlink_ext_ack *extack) 22118c2ecf20Sopenharmony_ci{ 22128c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci return mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex); 22158c2ecf20Sopenharmony_ci} 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_cistatic struct mlxsw_sp_fid * 22188c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021d_fid_lookup(struct mlxsw_sp_bridge_device *bridge_device, 22198c2ecf20Sopenharmony_ci u16 vid) 22208c2ecf20Sopenharmony_ci{ 22218c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci /* The only valid VLAN for a VLAN-unaware bridge is 0 */ 22248c2ecf20Sopenharmony_ci if (vid) 22258c2ecf20Sopenharmony_ci return NULL; 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ci return mlxsw_sp_fid_8021d_lookup(mlxsw_sp, bridge_device->dev->ifindex); 22288c2ecf20Sopenharmony_ci} 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_cistatic u16 22318c2ecf20Sopenharmony_cimlxsw_sp_bridge_8021d_fid_vid(struct mlxsw_sp_bridge_device *bridge_device, 22328c2ecf20Sopenharmony_ci const struct mlxsw_sp_fid *fid) 22338c2ecf20Sopenharmony_ci{ 22348c2ecf20Sopenharmony_ci return 0; 22358c2ecf20Sopenharmony_ci} 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021d_ops = { 22388c2ecf20Sopenharmony_ci .port_join = mlxsw_sp_bridge_8021d_port_join, 22398c2ecf20Sopenharmony_ci .port_leave = mlxsw_sp_bridge_8021d_port_leave, 22408c2ecf20Sopenharmony_ci .vxlan_join = mlxsw_sp_bridge_8021d_vxlan_join, 22418c2ecf20Sopenharmony_ci .fid_get = mlxsw_sp_bridge_8021d_fid_get, 22428c2ecf20Sopenharmony_ci .fid_lookup = mlxsw_sp_bridge_8021d_fid_lookup, 22438c2ecf20Sopenharmony_ci .fid_vid = mlxsw_sp_bridge_8021d_fid_vid, 22448c2ecf20Sopenharmony_ci}; 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ciint mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port, 22478c2ecf20Sopenharmony_ci struct net_device *brport_dev, 22488c2ecf20Sopenharmony_ci struct net_device *br_dev, 22498c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 22508c2ecf20Sopenharmony_ci{ 22518c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 22528c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 22538c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 22548c2ecf20Sopenharmony_ci int err; 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_get(mlxsw_sp->bridge, brport_dev, 22578c2ecf20Sopenharmony_ci extack); 22588c2ecf20Sopenharmony_ci if (IS_ERR(bridge_port)) 22598c2ecf20Sopenharmony_ci return PTR_ERR(bridge_port); 22608c2ecf20Sopenharmony_ci bridge_device = bridge_port->bridge_device; 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci err = bridge_device->ops->port_join(bridge_device, bridge_port, 22638c2ecf20Sopenharmony_ci mlxsw_sp_port, extack); 22648c2ecf20Sopenharmony_ci if (err) 22658c2ecf20Sopenharmony_ci goto err_port_join; 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci return 0; 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_cierr_port_join: 22708c2ecf20Sopenharmony_ci mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port); 22718c2ecf20Sopenharmony_ci return err; 22728c2ecf20Sopenharmony_ci} 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_civoid mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port, 22758c2ecf20Sopenharmony_ci struct net_device *brport_dev, 22768c2ecf20Sopenharmony_ci struct net_device *br_dev) 22778c2ecf20Sopenharmony_ci{ 22788c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 22798c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 22808c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 22838c2ecf20Sopenharmony_ci if (!bridge_device) 22848c2ecf20Sopenharmony_ci return; 22858c2ecf20Sopenharmony_ci bridge_port = __mlxsw_sp_bridge_port_find(bridge_device, brport_dev); 22868c2ecf20Sopenharmony_ci if (!bridge_port) 22878c2ecf20Sopenharmony_ci return; 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci bridge_device->ops->port_leave(bridge_device, bridge_port, 22908c2ecf20Sopenharmony_ci mlxsw_sp_port); 22918c2ecf20Sopenharmony_ci mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port); 22928c2ecf20Sopenharmony_ci} 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ciint mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp, 22958c2ecf20Sopenharmony_ci const struct net_device *br_dev, 22968c2ecf20Sopenharmony_ci const struct net_device *vxlan_dev, u16 vid, 22978c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 22988c2ecf20Sopenharmony_ci{ 22998c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 23028c2ecf20Sopenharmony_ci if (WARN_ON(!bridge_device)) 23038c2ecf20Sopenharmony_ci return -EINVAL; 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci return bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, vid, 23068c2ecf20Sopenharmony_ci extack); 23078c2ecf20Sopenharmony_ci} 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_civoid mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp, 23108c2ecf20Sopenharmony_ci const struct net_device *vxlan_dev) 23118c2ecf20Sopenharmony_ci{ 23128c2ecf20Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); 23138c2ecf20Sopenharmony_ci struct mlxsw_sp_fid *fid; 23148c2ecf20Sopenharmony_ci 23158c2ecf20Sopenharmony_ci /* If the VxLAN device is down, then the FID does not have a VNI */ 23168c2ecf20Sopenharmony_ci fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan->cfg.vni); 23178c2ecf20Sopenharmony_ci if (!fid) 23188c2ecf20Sopenharmony_ci return; 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci mlxsw_sp_nve_fid_disable(mlxsw_sp, fid); 23218c2ecf20Sopenharmony_ci /* Drop both the reference we just took during lookup and the reference 23228c2ecf20Sopenharmony_ci * the VXLAN device took. 23238c2ecf20Sopenharmony_ci */ 23248c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 23258c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 23268c2ecf20Sopenharmony_ci} 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_cistatic void 23298c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_addr_convert(const union vxlan_addr *vxlan_addr, 23308c2ecf20Sopenharmony_ci enum mlxsw_sp_l3proto *proto, 23318c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr *addr) 23328c2ecf20Sopenharmony_ci{ 23338c2ecf20Sopenharmony_ci if (vxlan_addr->sa.sa_family == AF_INET) { 23348c2ecf20Sopenharmony_ci addr->addr4 = vxlan_addr->sin.sin_addr.s_addr; 23358c2ecf20Sopenharmony_ci *proto = MLXSW_SP_L3_PROTO_IPV4; 23368c2ecf20Sopenharmony_ci } else { 23378c2ecf20Sopenharmony_ci addr->addr6 = vxlan_addr->sin6.sin6_addr; 23388c2ecf20Sopenharmony_ci *proto = MLXSW_SP_L3_PROTO_IPV6; 23398c2ecf20Sopenharmony_ci } 23408c2ecf20Sopenharmony_ci} 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_cistatic void 23438c2ecf20Sopenharmony_cimlxsw_sp_switchdev_addr_vxlan_convert(enum mlxsw_sp_l3proto proto, 23448c2ecf20Sopenharmony_ci const union mlxsw_sp_l3addr *addr, 23458c2ecf20Sopenharmony_ci union vxlan_addr *vxlan_addr) 23468c2ecf20Sopenharmony_ci{ 23478c2ecf20Sopenharmony_ci switch (proto) { 23488c2ecf20Sopenharmony_ci case MLXSW_SP_L3_PROTO_IPV4: 23498c2ecf20Sopenharmony_ci vxlan_addr->sa.sa_family = AF_INET; 23508c2ecf20Sopenharmony_ci vxlan_addr->sin.sin_addr.s_addr = addr->addr4; 23518c2ecf20Sopenharmony_ci break; 23528c2ecf20Sopenharmony_ci case MLXSW_SP_L3_PROTO_IPV6: 23538c2ecf20Sopenharmony_ci vxlan_addr->sa.sa_family = AF_INET6; 23548c2ecf20Sopenharmony_ci vxlan_addr->sin6.sin6_addr = addr->addr6; 23558c2ecf20Sopenharmony_ci break; 23568c2ecf20Sopenharmony_ci } 23578c2ecf20Sopenharmony_ci} 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_vxlan_call_notifiers(struct net_device *dev, 23608c2ecf20Sopenharmony_ci const char *mac, 23618c2ecf20Sopenharmony_ci enum mlxsw_sp_l3proto proto, 23628c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr *addr, 23638c2ecf20Sopenharmony_ci __be32 vni, bool adding) 23648c2ecf20Sopenharmony_ci{ 23658c2ecf20Sopenharmony_ci struct switchdev_notifier_vxlan_fdb_info info; 23668c2ecf20Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(dev); 23678c2ecf20Sopenharmony_ci enum switchdev_notifier_type type; 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE : 23708c2ecf20Sopenharmony_ci SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE; 23718c2ecf20Sopenharmony_ci mlxsw_sp_switchdev_addr_vxlan_convert(proto, addr, &info.remote_ip); 23728c2ecf20Sopenharmony_ci info.remote_port = vxlan->cfg.dst_port; 23738c2ecf20Sopenharmony_ci info.remote_vni = vni; 23748c2ecf20Sopenharmony_ci info.remote_ifindex = 0; 23758c2ecf20Sopenharmony_ci ether_addr_copy(info.eth_addr, mac); 23768c2ecf20Sopenharmony_ci info.vni = vni; 23778c2ecf20Sopenharmony_ci info.offloaded = adding; 23788c2ecf20Sopenharmony_ci call_switchdev_notifiers(type, dev, &info.info, NULL); 23798c2ecf20Sopenharmony_ci} 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_nve_call_notifiers(struct net_device *dev, 23828c2ecf20Sopenharmony_ci const char *mac, 23838c2ecf20Sopenharmony_ci enum mlxsw_sp_l3proto proto, 23848c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr *addr, 23858c2ecf20Sopenharmony_ci __be32 vni, 23868c2ecf20Sopenharmony_ci bool adding) 23878c2ecf20Sopenharmony_ci{ 23888c2ecf20Sopenharmony_ci if (netif_is_vxlan(dev)) 23898c2ecf20Sopenharmony_ci mlxsw_sp_fdb_vxlan_call_notifiers(dev, mac, proto, addr, vni, 23908c2ecf20Sopenharmony_ci adding); 23918c2ecf20Sopenharmony_ci} 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_cistatic void 23948c2ecf20Sopenharmony_cimlxsw_sp_fdb_call_notifiers(enum switchdev_notifier_type type, 23958c2ecf20Sopenharmony_ci const char *mac, u16 vid, 23968c2ecf20Sopenharmony_ci struct net_device *dev, bool offloaded) 23978c2ecf20Sopenharmony_ci{ 23988c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info info; 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci info.addr = mac; 24018c2ecf20Sopenharmony_ci info.vid = vid; 24028c2ecf20Sopenharmony_ci info.offloaded = offloaded; 24038c2ecf20Sopenharmony_ci call_switchdev_notifiers(type, dev, &info.info, NULL); 24048c2ecf20Sopenharmony_ci} 24058c2ecf20Sopenharmony_ci 24068c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, 24078c2ecf20Sopenharmony_ci char *sfn_pl, int rec_index, 24088c2ecf20Sopenharmony_ci bool adding) 24098c2ecf20Sopenharmony_ci{ 24108c2ecf20Sopenharmony_ci unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core); 24118c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 24128c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 24138c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 24148c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 24158c2ecf20Sopenharmony_ci enum switchdev_notifier_type type; 24168c2ecf20Sopenharmony_ci char mac[ETH_ALEN]; 24178c2ecf20Sopenharmony_ci u8 local_port; 24188c2ecf20Sopenharmony_ci u16 vid, fid; 24198c2ecf20Sopenharmony_ci bool do_notification = true; 24208c2ecf20Sopenharmony_ci int err; 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &fid, &local_port); 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(local_port >= max_ports)) 24258c2ecf20Sopenharmony_ci return; 24268c2ecf20Sopenharmony_ci mlxsw_sp_port = mlxsw_sp->ports[local_port]; 24278c2ecf20Sopenharmony_ci if (!mlxsw_sp_port) { 24288c2ecf20Sopenharmony_ci dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect local port in FDB notification\n"); 24298c2ecf20Sopenharmony_ci goto just_remove; 24308c2ecf20Sopenharmony_ci } 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci if (mlxsw_sp_fid_is_dummy(mlxsw_sp, fid)) 24338c2ecf20Sopenharmony_ci goto just_remove; 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_fid(mlxsw_sp_port, fid); 24368c2ecf20Sopenharmony_ci if (!mlxsw_sp_port_vlan) { 24378c2ecf20Sopenharmony_ci netdev_err(mlxsw_sp_port->dev, "Failed to find a matching {Port, VID} following FDB notification\n"); 24388c2ecf20Sopenharmony_ci goto just_remove; 24398c2ecf20Sopenharmony_ci } 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_port_vlan->bridge_port; 24428c2ecf20Sopenharmony_ci if (!bridge_port) { 24438c2ecf20Sopenharmony_ci netdev_err(mlxsw_sp_port->dev, "{Port, VID} not associated with a bridge\n"); 24448c2ecf20Sopenharmony_ci goto just_remove; 24458c2ecf20Sopenharmony_ci } 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci bridge_device = bridge_port->bridge_device; 24488c2ecf20Sopenharmony_ci vid = bridge_device->vlan_enabled ? mlxsw_sp_port_vlan->vid : 0; 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_cido_fdb_op: 24518c2ecf20Sopenharmony_ci err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid, 24528c2ecf20Sopenharmony_ci adding, true); 24538c2ecf20Sopenharmony_ci if (err) { 24548c2ecf20Sopenharmony_ci dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to set FDB entry\n"); 24558c2ecf20Sopenharmony_ci return; 24568c2ecf20Sopenharmony_ci } 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_ci if (!do_notification) 24598c2ecf20Sopenharmony_ci return; 24608c2ecf20Sopenharmony_ci type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; 24618c2ecf20Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev, adding); 24628c2ecf20Sopenharmony_ci 24638c2ecf20Sopenharmony_ci return; 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_cijust_remove: 24668c2ecf20Sopenharmony_ci adding = false; 24678c2ecf20Sopenharmony_ci do_notification = false; 24688c2ecf20Sopenharmony_ci goto do_fdb_op; 24698c2ecf20Sopenharmony_ci} 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, 24728c2ecf20Sopenharmony_ci char *sfn_pl, int rec_index, 24738c2ecf20Sopenharmony_ci bool adding) 24748c2ecf20Sopenharmony_ci{ 24758c2ecf20Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 24768c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 24778c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 24788c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 24798c2ecf20Sopenharmony_ci enum switchdev_notifier_type type; 24808c2ecf20Sopenharmony_ci char mac[ETH_ALEN]; 24818c2ecf20Sopenharmony_ci u16 lag_vid = 0; 24828c2ecf20Sopenharmony_ci u16 lag_id; 24838c2ecf20Sopenharmony_ci u16 vid, fid; 24848c2ecf20Sopenharmony_ci bool do_notification = true; 24858c2ecf20Sopenharmony_ci int err; 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_ci mlxsw_reg_sfn_mac_lag_unpack(sfn_pl, rec_index, mac, &fid, &lag_id); 24888c2ecf20Sopenharmony_ci mlxsw_sp_port = mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id); 24898c2ecf20Sopenharmony_ci if (!mlxsw_sp_port) { 24908c2ecf20Sopenharmony_ci dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Cannot find port representor for LAG\n"); 24918c2ecf20Sopenharmony_ci goto just_remove; 24928c2ecf20Sopenharmony_ci } 24938c2ecf20Sopenharmony_ci 24948c2ecf20Sopenharmony_ci if (mlxsw_sp_fid_is_dummy(mlxsw_sp, fid)) 24958c2ecf20Sopenharmony_ci goto just_remove; 24968c2ecf20Sopenharmony_ci 24978c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_fid(mlxsw_sp_port, fid); 24988c2ecf20Sopenharmony_ci if (!mlxsw_sp_port_vlan) { 24998c2ecf20Sopenharmony_ci netdev_err(mlxsw_sp_port->dev, "Failed to find a matching {Port, VID} following FDB notification\n"); 25008c2ecf20Sopenharmony_ci goto just_remove; 25018c2ecf20Sopenharmony_ci } 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_port_vlan->bridge_port; 25048c2ecf20Sopenharmony_ci if (!bridge_port) { 25058c2ecf20Sopenharmony_ci netdev_err(mlxsw_sp_port->dev, "{Port, VID} not associated with a bridge\n"); 25068c2ecf20Sopenharmony_ci goto just_remove; 25078c2ecf20Sopenharmony_ci } 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_ci bridge_device = bridge_port->bridge_device; 25108c2ecf20Sopenharmony_ci vid = bridge_device->vlan_enabled ? mlxsw_sp_port_vlan->vid : 0; 25118c2ecf20Sopenharmony_ci lag_vid = mlxsw_sp_fid_lag_vid_valid(mlxsw_sp_port_vlan->fid) ? 25128c2ecf20Sopenharmony_ci mlxsw_sp_port_vlan->vid : 0; 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_cido_fdb_op: 25158c2ecf20Sopenharmony_ci err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid, 25168c2ecf20Sopenharmony_ci adding, true); 25178c2ecf20Sopenharmony_ci if (err) { 25188c2ecf20Sopenharmony_ci dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to set FDB entry\n"); 25198c2ecf20Sopenharmony_ci return; 25208c2ecf20Sopenharmony_ci } 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_ci if (!do_notification) 25238c2ecf20Sopenharmony_ci return; 25248c2ecf20Sopenharmony_ci type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; 25258c2ecf20Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev, adding); 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_ci return; 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_cijust_remove: 25308c2ecf20Sopenharmony_ci adding = false; 25318c2ecf20Sopenharmony_ci do_notification = false; 25328c2ecf20Sopenharmony_ci goto do_fdb_op; 25338c2ecf20Sopenharmony_ci} 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_cistatic int 25368c2ecf20Sopenharmony_ci__mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp, 25378c2ecf20Sopenharmony_ci const struct mlxsw_sp_fid *fid, 25388c2ecf20Sopenharmony_ci bool adding, 25398c2ecf20Sopenharmony_ci struct net_device **nve_dev, 25408c2ecf20Sopenharmony_ci u16 *p_vid, __be32 *p_vni) 25418c2ecf20Sopenharmony_ci{ 25428c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 25438c2ecf20Sopenharmony_ci struct net_device *br_dev, *dev; 25448c2ecf20Sopenharmony_ci int nve_ifindex; 25458c2ecf20Sopenharmony_ci int err; 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci err = mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex); 25488c2ecf20Sopenharmony_ci if (err) 25498c2ecf20Sopenharmony_ci return err; 25508c2ecf20Sopenharmony_ci 25518c2ecf20Sopenharmony_ci err = mlxsw_sp_fid_vni(fid, p_vni); 25528c2ecf20Sopenharmony_ci if (err) 25538c2ecf20Sopenharmony_ci return err; 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci dev = __dev_get_by_index(mlxsw_sp_net(mlxsw_sp), nve_ifindex); 25568c2ecf20Sopenharmony_ci if (!dev) 25578c2ecf20Sopenharmony_ci return -EINVAL; 25588c2ecf20Sopenharmony_ci *nve_dev = dev; 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci if (!netif_running(dev)) 25618c2ecf20Sopenharmony_ci return -EINVAL; 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci if (adding && !br_port_flag_is_set(dev, BR_LEARNING)) 25648c2ecf20Sopenharmony_ci return -EINVAL; 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci if (adding && netif_is_vxlan(dev)) { 25678c2ecf20Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(dev); 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci if (!(vxlan->cfg.flags & VXLAN_F_LEARN)) 25708c2ecf20Sopenharmony_ci return -EINVAL; 25718c2ecf20Sopenharmony_ci } 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci br_dev = netdev_master_upper_dev_get(dev); 25748c2ecf20Sopenharmony_ci if (!br_dev) 25758c2ecf20Sopenharmony_ci return -EINVAL; 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 25788c2ecf20Sopenharmony_ci if (!bridge_device) 25798c2ecf20Sopenharmony_ci return -EINVAL; 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_ci *p_vid = bridge_device->ops->fid_vid(bridge_device, fid); 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_ci return 0; 25848c2ecf20Sopenharmony_ci} 25858c2ecf20Sopenharmony_ci 25868c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp, 25878c2ecf20Sopenharmony_ci char *sfn_pl, 25888c2ecf20Sopenharmony_ci int rec_index, 25898c2ecf20Sopenharmony_ci bool adding) 25908c2ecf20Sopenharmony_ci{ 25918c2ecf20Sopenharmony_ci enum mlxsw_reg_sfn_uc_tunnel_protocol sfn_proto; 25928c2ecf20Sopenharmony_ci enum switchdev_notifier_type type; 25938c2ecf20Sopenharmony_ci struct net_device *nve_dev; 25948c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr addr; 25958c2ecf20Sopenharmony_ci struct mlxsw_sp_fid *fid; 25968c2ecf20Sopenharmony_ci char mac[ETH_ALEN]; 25978c2ecf20Sopenharmony_ci u16 fid_index, vid; 25988c2ecf20Sopenharmony_ci __be32 vni; 25998c2ecf20Sopenharmony_ci u32 uip; 26008c2ecf20Sopenharmony_ci int err; 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ci mlxsw_reg_sfn_uc_tunnel_unpack(sfn_pl, rec_index, mac, &fid_index, 26038c2ecf20Sopenharmony_ci &uip, &sfn_proto); 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci fid = mlxsw_sp_fid_lookup_by_index(mlxsw_sp, fid_index); 26068c2ecf20Sopenharmony_ci if (!fid) 26078c2ecf20Sopenharmony_ci goto err_fid_lookup; 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci err = mlxsw_sp_nve_learned_ip_resolve(mlxsw_sp, uip, 26108c2ecf20Sopenharmony_ci (enum mlxsw_sp_l3proto) sfn_proto, 26118c2ecf20Sopenharmony_ci &addr); 26128c2ecf20Sopenharmony_ci if (err) 26138c2ecf20Sopenharmony_ci goto err_ip_resolve; 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci err = __mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, fid, adding, 26168c2ecf20Sopenharmony_ci &nve_dev, &vid, &vni); 26178c2ecf20Sopenharmony_ci if (err) 26188c2ecf20Sopenharmony_ci goto err_fdb_process; 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, mac, fid_index, 26218c2ecf20Sopenharmony_ci (enum mlxsw_sp_l3proto) sfn_proto, 26228c2ecf20Sopenharmony_ci &addr, adding, true); 26238c2ecf20Sopenharmony_ci if (err) 26248c2ecf20Sopenharmony_ci goto err_fdb_op; 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_ci mlxsw_sp_fdb_nve_call_notifiers(nve_dev, mac, 26278c2ecf20Sopenharmony_ci (enum mlxsw_sp_l3proto) sfn_proto, 26288c2ecf20Sopenharmony_ci &addr, vni, adding); 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_ci type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : 26318c2ecf20Sopenharmony_ci SWITCHDEV_FDB_DEL_TO_BRIDGE; 26328c2ecf20Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(type, mac, vid, nve_dev, adding); 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci return; 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_cierr_fdb_op: 26398c2ecf20Sopenharmony_cierr_fdb_process: 26408c2ecf20Sopenharmony_cierr_ip_resolve: 26418c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 26428c2ecf20Sopenharmony_cierr_fid_lookup: 26438c2ecf20Sopenharmony_ci /* Remove an FDB entry in case we cannot process it. Otherwise the 26448c2ecf20Sopenharmony_ci * device will keep sending the same notification over and over again. 26458c2ecf20Sopenharmony_ci */ 26468c2ecf20Sopenharmony_ci mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, mac, fid_index, 26478c2ecf20Sopenharmony_ci (enum mlxsw_sp_l3proto) sfn_proto, &addr, 26488c2ecf20Sopenharmony_ci false, true); 26498c2ecf20Sopenharmony_ci} 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp, 26528c2ecf20Sopenharmony_ci char *sfn_pl, int rec_index) 26538c2ecf20Sopenharmony_ci{ 26548c2ecf20Sopenharmony_ci switch (mlxsw_reg_sfn_rec_type_get(sfn_pl, rec_index)) { 26558c2ecf20Sopenharmony_ci case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC: 26568c2ecf20Sopenharmony_ci mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl, 26578c2ecf20Sopenharmony_ci rec_index, true); 26588c2ecf20Sopenharmony_ci break; 26598c2ecf20Sopenharmony_ci case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC: 26608c2ecf20Sopenharmony_ci mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl, 26618c2ecf20Sopenharmony_ci rec_index, false); 26628c2ecf20Sopenharmony_ci break; 26638c2ecf20Sopenharmony_ci case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC_LAG: 26648c2ecf20Sopenharmony_ci mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl, 26658c2ecf20Sopenharmony_ci rec_index, true); 26668c2ecf20Sopenharmony_ci break; 26678c2ecf20Sopenharmony_ci case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC_LAG: 26688c2ecf20Sopenharmony_ci mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl, 26698c2ecf20Sopenharmony_ci rec_index, false); 26708c2ecf20Sopenharmony_ci break; 26718c2ecf20Sopenharmony_ci case MLXSW_REG_SFN_REC_TYPE_LEARNED_UNICAST_TUNNEL: 26728c2ecf20Sopenharmony_ci mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, sfn_pl, 26738c2ecf20Sopenharmony_ci rec_index, true); 26748c2ecf20Sopenharmony_ci break; 26758c2ecf20Sopenharmony_ci case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_UNICAST_TUNNEL: 26768c2ecf20Sopenharmony_ci mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, sfn_pl, 26778c2ecf20Sopenharmony_ci rec_index, false); 26788c2ecf20Sopenharmony_ci break; 26798c2ecf20Sopenharmony_ci } 26808c2ecf20Sopenharmony_ci} 26818c2ecf20Sopenharmony_ci 26828c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_notify_work_schedule(struct mlxsw_sp *mlxsw_sp, 26838c2ecf20Sopenharmony_ci bool no_delay) 26848c2ecf20Sopenharmony_ci{ 26858c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge; 26868c2ecf20Sopenharmony_ci unsigned int interval = no_delay ? 0 : bridge->fdb_notify.interval; 26878c2ecf20Sopenharmony_ci 26888c2ecf20Sopenharmony_ci mlxsw_core_schedule_dw(&bridge->fdb_notify.dw, 26898c2ecf20Sopenharmony_ci msecs_to_jiffies(interval)); 26908c2ecf20Sopenharmony_ci} 26918c2ecf20Sopenharmony_ci 26928c2ecf20Sopenharmony_ci#define MLXSW_SP_FDB_SFN_QUERIES_PER_SESSION 10 26938c2ecf20Sopenharmony_ci 26948c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_notify_work(struct work_struct *work) 26958c2ecf20Sopenharmony_ci{ 26968c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge *bridge; 26978c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 26988c2ecf20Sopenharmony_ci char *sfn_pl; 26998c2ecf20Sopenharmony_ci int queries; 27008c2ecf20Sopenharmony_ci u8 num_rec; 27018c2ecf20Sopenharmony_ci int i; 27028c2ecf20Sopenharmony_ci int err; 27038c2ecf20Sopenharmony_ci 27048c2ecf20Sopenharmony_ci sfn_pl = kmalloc(MLXSW_REG_SFN_LEN, GFP_KERNEL); 27058c2ecf20Sopenharmony_ci if (!sfn_pl) 27068c2ecf20Sopenharmony_ci return; 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_ci bridge = container_of(work, struct mlxsw_sp_bridge, fdb_notify.dw.work); 27098c2ecf20Sopenharmony_ci mlxsw_sp = bridge->mlxsw_sp; 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_ci rtnl_lock(); 27128c2ecf20Sopenharmony_ci queries = MLXSW_SP_FDB_SFN_QUERIES_PER_SESSION; 27138c2ecf20Sopenharmony_ci while (queries > 0) { 27148c2ecf20Sopenharmony_ci mlxsw_reg_sfn_pack(sfn_pl); 27158c2ecf20Sopenharmony_ci err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl); 27168c2ecf20Sopenharmony_ci if (err) { 27178c2ecf20Sopenharmony_ci dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get FDB notifications\n"); 27188c2ecf20Sopenharmony_ci goto out; 27198c2ecf20Sopenharmony_ci } 27208c2ecf20Sopenharmony_ci num_rec = mlxsw_reg_sfn_num_rec_get(sfn_pl); 27218c2ecf20Sopenharmony_ci for (i = 0; i < num_rec; i++) 27228c2ecf20Sopenharmony_ci mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i); 27238c2ecf20Sopenharmony_ci if (num_rec != MLXSW_REG_SFN_REC_MAX_COUNT) 27248c2ecf20Sopenharmony_ci goto out; 27258c2ecf20Sopenharmony_ci queries--; 27268c2ecf20Sopenharmony_ci } 27278c2ecf20Sopenharmony_ci 27288c2ecf20Sopenharmony_ciout: 27298c2ecf20Sopenharmony_ci rtnl_unlock(); 27308c2ecf20Sopenharmony_ci kfree(sfn_pl); 27318c2ecf20Sopenharmony_ci mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp, !queries); 27328c2ecf20Sopenharmony_ci} 27338c2ecf20Sopenharmony_ci 27348c2ecf20Sopenharmony_cistruct mlxsw_sp_switchdev_event_work { 27358c2ecf20Sopenharmony_ci struct work_struct work; 27368c2ecf20Sopenharmony_ci union { 27378c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info fdb_info; 27388c2ecf20Sopenharmony_ci struct switchdev_notifier_vxlan_fdb_info vxlan_fdb_info; 27398c2ecf20Sopenharmony_ci }; 27408c2ecf20Sopenharmony_ci struct net_device *dev; 27418c2ecf20Sopenharmony_ci unsigned long event; 27428c2ecf20Sopenharmony_ci}; 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_cistatic void 27458c2ecf20Sopenharmony_cimlxsw_sp_switchdev_bridge_vxlan_fdb_event(struct mlxsw_sp *mlxsw_sp, 27468c2ecf20Sopenharmony_ci struct mlxsw_sp_switchdev_event_work * 27478c2ecf20Sopenharmony_ci switchdev_work, 27488c2ecf20Sopenharmony_ci struct mlxsw_sp_fid *fid, __be32 vni) 27498c2ecf20Sopenharmony_ci{ 27508c2ecf20Sopenharmony_ci struct switchdev_notifier_vxlan_fdb_info vxlan_fdb_info; 27518c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info; 27528c2ecf20Sopenharmony_ci struct net_device *dev = switchdev_work->dev; 27538c2ecf20Sopenharmony_ci enum mlxsw_sp_l3proto proto; 27548c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr addr; 27558c2ecf20Sopenharmony_ci int err; 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci fdb_info = &switchdev_work->fdb_info; 27588c2ecf20Sopenharmony_ci err = vxlan_fdb_find_uc(dev, fdb_info->addr, vni, &vxlan_fdb_info); 27598c2ecf20Sopenharmony_ci if (err) 27608c2ecf20Sopenharmony_ci return; 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_ci mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info.remote_ip, 27638c2ecf20Sopenharmony_ci &proto, &addr); 27648c2ecf20Sopenharmony_ci 27658c2ecf20Sopenharmony_ci switch (switchdev_work->event) { 27668c2ecf20Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 27678c2ecf20Sopenharmony_ci err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, 27688c2ecf20Sopenharmony_ci vxlan_fdb_info.eth_addr, 27698c2ecf20Sopenharmony_ci mlxsw_sp_fid_index(fid), 27708c2ecf20Sopenharmony_ci proto, &addr, true, false); 27718c2ecf20Sopenharmony_ci if (err) 27728c2ecf20Sopenharmony_ci return; 27738c2ecf20Sopenharmony_ci vxlan_fdb_info.offloaded = true; 27748c2ecf20Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, 27758c2ecf20Sopenharmony_ci &vxlan_fdb_info.info, NULL); 27768c2ecf20Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, 27778c2ecf20Sopenharmony_ci vxlan_fdb_info.eth_addr, 27788c2ecf20Sopenharmony_ci fdb_info->vid, dev, true); 27798c2ecf20Sopenharmony_ci break; 27808c2ecf20Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 27818c2ecf20Sopenharmony_ci err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, 27828c2ecf20Sopenharmony_ci vxlan_fdb_info.eth_addr, 27838c2ecf20Sopenharmony_ci mlxsw_sp_fid_index(fid), 27848c2ecf20Sopenharmony_ci proto, &addr, false, 27858c2ecf20Sopenharmony_ci false); 27868c2ecf20Sopenharmony_ci vxlan_fdb_info.offloaded = false; 27878c2ecf20Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, 27888c2ecf20Sopenharmony_ci &vxlan_fdb_info.info, NULL); 27898c2ecf20Sopenharmony_ci break; 27908c2ecf20Sopenharmony_ci } 27918c2ecf20Sopenharmony_ci} 27928c2ecf20Sopenharmony_ci 27938c2ecf20Sopenharmony_cistatic void 27948c2ecf20Sopenharmony_cimlxsw_sp_switchdev_bridge_nve_fdb_event(struct mlxsw_sp_switchdev_event_work * 27958c2ecf20Sopenharmony_ci switchdev_work) 27968c2ecf20Sopenharmony_ci{ 27978c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 27988c2ecf20Sopenharmony_ci struct net_device *dev = switchdev_work->dev; 27998c2ecf20Sopenharmony_ci struct net_device *br_dev; 28008c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 28018c2ecf20Sopenharmony_ci struct mlxsw_sp_fid *fid; 28028c2ecf20Sopenharmony_ci __be32 vni; 28038c2ecf20Sopenharmony_ci int err; 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ci if (switchdev_work->event != SWITCHDEV_FDB_ADD_TO_DEVICE && 28068c2ecf20Sopenharmony_ci switchdev_work->event != SWITCHDEV_FDB_DEL_TO_DEVICE) 28078c2ecf20Sopenharmony_ci return; 28088c2ecf20Sopenharmony_ci 28098c2ecf20Sopenharmony_ci if (switchdev_work->event == SWITCHDEV_FDB_ADD_TO_DEVICE && 28108c2ecf20Sopenharmony_ci !switchdev_work->fdb_info.added_by_user) 28118c2ecf20Sopenharmony_ci return; 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_ci if (!netif_running(dev)) 28148c2ecf20Sopenharmony_ci return; 28158c2ecf20Sopenharmony_ci br_dev = netdev_master_upper_dev_get(dev); 28168c2ecf20Sopenharmony_ci if (!br_dev) 28178c2ecf20Sopenharmony_ci return; 28188c2ecf20Sopenharmony_ci if (!netif_is_bridge_master(br_dev)) 28198c2ecf20Sopenharmony_ci return; 28208c2ecf20Sopenharmony_ci mlxsw_sp = mlxsw_sp_lower_get(br_dev); 28218c2ecf20Sopenharmony_ci if (!mlxsw_sp) 28228c2ecf20Sopenharmony_ci return; 28238c2ecf20Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 28248c2ecf20Sopenharmony_ci if (!bridge_device) 28258c2ecf20Sopenharmony_ci return; 28268c2ecf20Sopenharmony_ci 28278c2ecf20Sopenharmony_ci fid = bridge_device->ops->fid_lookup(bridge_device, 28288c2ecf20Sopenharmony_ci switchdev_work->fdb_info.vid); 28298c2ecf20Sopenharmony_ci if (!fid) 28308c2ecf20Sopenharmony_ci return; 28318c2ecf20Sopenharmony_ci 28328c2ecf20Sopenharmony_ci err = mlxsw_sp_fid_vni(fid, &vni); 28338c2ecf20Sopenharmony_ci if (err) 28348c2ecf20Sopenharmony_ci goto out; 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_ci mlxsw_sp_switchdev_bridge_vxlan_fdb_event(mlxsw_sp, switchdev_work, fid, 28378c2ecf20Sopenharmony_ci vni); 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_ciout: 28408c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 28418c2ecf20Sopenharmony_ci} 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_cistatic void mlxsw_sp_switchdev_bridge_fdb_event_work(struct work_struct *work) 28448c2ecf20Sopenharmony_ci{ 28458c2ecf20Sopenharmony_ci struct mlxsw_sp_switchdev_event_work *switchdev_work = 28468c2ecf20Sopenharmony_ci container_of(work, struct mlxsw_sp_switchdev_event_work, work); 28478c2ecf20Sopenharmony_ci struct net_device *dev = switchdev_work->dev; 28488c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info; 28498c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 28508c2ecf20Sopenharmony_ci int err; 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci rtnl_lock(); 28538c2ecf20Sopenharmony_ci if (netif_is_vxlan(dev)) { 28548c2ecf20Sopenharmony_ci mlxsw_sp_switchdev_bridge_nve_fdb_event(switchdev_work); 28558c2ecf20Sopenharmony_ci goto out; 28568c2ecf20Sopenharmony_ci } 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(dev); 28598c2ecf20Sopenharmony_ci if (!mlxsw_sp_port) 28608c2ecf20Sopenharmony_ci goto out; 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci switch (switchdev_work->event) { 28638c2ecf20Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 28648c2ecf20Sopenharmony_ci fdb_info = &switchdev_work->fdb_info; 28658c2ecf20Sopenharmony_ci if (!fdb_info->added_by_user) 28668c2ecf20Sopenharmony_ci break; 28678c2ecf20Sopenharmony_ci err = mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, true); 28688c2ecf20Sopenharmony_ci if (err) 28698c2ecf20Sopenharmony_ci break; 28708c2ecf20Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, 28718c2ecf20Sopenharmony_ci fdb_info->addr, 28728c2ecf20Sopenharmony_ci fdb_info->vid, dev, true); 28738c2ecf20Sopenharmony_ci break; 28748c2ecf20Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 28758c2ecf20Sopenharmony_ci fdb_info = &switchdev_work->fdb_info; 28768c2ecf20Sopenharmony_ci mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, false); 28778c2ecf20Sopenharmony_ci break; 28788c2ecf20Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_BRIDGE: 28798c2ecf20Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_BRIDGE: 28808c2ecf20Sopenharmony_ci /* These events are only used to potentially update an existing 28818c2ecf20Sopenharmony_ci * SPAN mirror. 28828c2ecf20Sopenharmony_ci */ 28838c2ecf20Sopenharmony_ci break; 28848c2ecf20Sopenharmony_ci } 28858c2ecf20Sopenharmony_ci 28868c2ecf20Sopenharmony_ci mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp); 28878c2ecf20Sopenharmony_ci 28888c2ecf20Sopenharmony_ciout: 28898c2ecf20Sopenharmony_ci rtnl_unlock(); 28908c2ecf20Sopenharmony_ci kfree(switchdev_work->fdb_info.addr); 28918c2ecf20Sopenharmony_ci kfree(switchdev_work); 28928c2ecf20Sopenharmony_ci dev_put(dev); 28938c2ecf20Sopenharmony_ci} 28948c2ecf20Sopenharmony_ci 28958c2ecf20Sopenharmony_cistatic void 28968c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_fdb_add(struct mlxsw_sp *mlxsw_sp, 28978c2ecf20Sopenharmony_ci struct mlxsw_sp_switchdev_event_work * 28988c2ecf20Sopenharmony_ci switchdev_work) 28998c2ecf20Sopenharmony_ci{ 29008c2ecf20Sopenharmony_ci struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info; 29018c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 29028c2ecf20Sopenharmony_ci struct net_device *dev = switchdev_work->dev; 29038c2ecf20Sopenharmony_ci u8 all_zeros_mac[ETH_ALEN] = { 0 }; 29048c2ecf20Sopenharmony_ci enum mlxsw_sp_l3proto proto; 29058c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr addr; 29068c2ecf20Sopenharmony_ci struct net_device *br_dev; 29078c2ecf20Sopenharmony_ci struct mlxsw_sp_fid *fid; 29088c2ecf20Sopenharmony_ci u16 vid; 29098c2ecf20Sopenharmony_ci int err; 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_ci vxlan_fdb_info = &switchdev_work->vxlan_fdb_info; 29128c2ecf20Sopenharmony_ci br_dev = netdev_master_upper_dev_get(dev); 29138c2ecf20Sopenharmony_ci 29148c2ecf20Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 29158c2ecf20Sopenharmony_ci if (!bridge_device) 29168c2ecf20Sopenharmony_ci return; 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_ci fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan_fdb_info->vni); 29198c2ecf20Sopenharmony_ci if (!fid) 29208c2ecf20Sopenharmony_ci return; 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info->remote_ip, 29238c2ecf20Sopenharmony_ci &proto, &addr); 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_ci if (ether_addr_equal(vxlan_fdb_info->eth_addr, all_zeros_mac)) { 29268c2ecf20Sopenharmony_ci err = mlxsw_sp_nve_flood_ip_add(mlxsw_sp, fid, proto, &addr); 29278c2ecf20Sopenharmony_ci if (err) { 29288c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 29298c2ecf20Sopenharmony_ci return; 29308c2ecf20Sopenharmony_ci } 29318c2ecf20Sopenharmony_ci vxlan_fdb_info->offloaded = true; 29328c2ecf20Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, 29338c2ecf20Sopenharmony_ci &vxlan_fdb_info->info, NULL); 29348c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 29358c2ecf20Sopenharmony_ci return; 29368c2ecf20Sopenharmony_ci } 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ci /* The device has a single FDB table, whereas Linux has two - one 29398c2ecf20Sopenharmony_ci * in the bridge driver and another in the VxLAN driver. We only 29408c2ecf20Sopenharmony_ci * program an entry to the device if the MAC points to the VxLAN 29418c2ecf20Sopenharmony_ci * device in the bridge's FDB table 29428c2ecf20Sopenharmony_ci */ 29438c2ecf20Sopenharmony_ci vid = bridge_device->ops->fid_vid(bridge_device, fid); 29448c2ecf20Sopenharmony_ci if (br_fdb_find_port(br_dev, vxlan_fdb_info->eth_addr, vid) != dev) 29458c2ecf20Sopenharmony_ci goto err_br_fdb_find; 29468c2ecf20Sopenharmony_ci 29478c2ecf20Sopenharmony_ci err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, vxlan_fdb_info->eth_addr, 29488c2ecf20Sopenharmony_ci mlxsw_sp_fid_index(fid), proto, 29498c2ecf20Sopenharmony_ci &addr, true, false); 29508c2ecf20Sopenharmony_ci if (err) 29518c2ecf20Sopenharmony_ci goto err_fdb_tunnel_uc_op; 29528c2ecf20Sopenharmony_ci vxlan_fdb_info->offloaded = true; 29538c2ecf20Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, 29548c2ecf20Sopenharmony_ci &vxlan_fdb_info->info, NULL); 29558c2ecf20Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, 29568c2ecf20Sopenharmony_ci vxlan_fdb_info->eth_addr, vid, dev, true); 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_ci return; 29618c2ecf20Sopenharmony_ci 29628c2ecf20Sopenharmony_cierr_fdb_tunnel_uc_op: 29638c2ecf20Sopenharmony_cierr_br_fdb_find: 29648c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 29658c2ecf20Sopenharmony_ci} 29668c2ecf20Sopenharmony_ci 29678c2ecf20Sopenharmony_cistatic void 29688c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_fdb_del(struct mlxsw_sp *mlxsw_sp, 29698c2ecf20Sopenharmony_ci struct mlxsw_sp_switchdev_event_work * 29708c2ecf20Sopenharmony_ci switchdev_work) 29718c2ecf20Sopenharmony_ci{ 29728c2ecf20Sopenharmony_ci struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info; 29738c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 29748c2ecf20Sopenharmony_ci struct net_device *dev = switchdev_work->dev; 29758c2ecf20Sopenharmony_ci struct net_device *br_dev = netdev_master_upper_dev_get(dev); 29768c2ecf20Sopenharmony_ci u8 all_zeros_mac[ETH_ALEN] = { 0 }; 29778c2ecf20Sopenharmony_ci enum mlxsw_sp_l3proto proto; 29788c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr addr; 29798c2ecf20Sopenharmony_ci struct mlxsw_sp_fid *fid; 29808c2ecf20Sopenharmony_ci u16 vid; 29818c2ecf20Sopenharmony_ci 29828c2ecf20Sopenharmony_ci vxlan_fdb_info = &switchdev_work->vxlan_fdb_info; 29838c2ecf20Sopenharmony_ci 29848c2ecf20Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 29858c2ecf20Sopenharmony_ci if (!bridge_device) 29868c2ecf20Sopenharmony_ci return; 29878c2ecf20Sopenharmony_ci 29888c2ecf20Sopenharmony_ci fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan_fdb_info->vni); 29898c2ecf20Sopenharmony_ci if (!fid) 29908c2ecf20Sopenharmony_ci return; 29918c2ecf20Sopenharmony_ci 29928c2ecf20Sopenharmony_ci mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info->remote_ip, 29938c2ecf20Sopenharmony_ci &proto, &addr); 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_ci if (ether_addr_equal(vxlan_fdb_info->eth_addr, all_zeros_mac)) { 29968c2ecf20Sopenharmony_ci mlxsw_sp_nve_flood_ip_del(mlxsw_sp, fid, proto, &addr); 29978c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 29988c2ecf20Sopenharmony_ci return; 29998c2ecf20Sopenharmony_ci } 30008c2ecf20Sopenharmony_ci 30018c2ecf20Sopenharmony_ci mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, vxlan_fdb_info->eth_addr, 30028c2ecf20Sopenharmony_ci mlxsw_sp_fid_index(fid), proto, &addr, 30038c2ecf20Sopenharmony_ci false, false); 30048c2ecf20Sopenharmony_ci vid = bridge_device->ops->fid_vid(bridge_device, fid); 30058c2ecf20Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, 30068c2ecf20Sopenharmony_ci vxlan_fdb_info->eth_addr, vid, dev, false); 30078c2ecf20Sopenharmony_ci 30088c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 30098c2ecf20Sopenharmony_ci} 30108c2ecf20Sopenharmony_ci 30118c2ecf20Sopenharmony_cistatic void mlxsw_sp_switchdev_vxlan_fdb_event_work(struct work_struct *work) 30128c2ecf20Sopenharmony_ci{ 30138c2ecf20Sopenharmony_ci struct mlxsw_sp_switchdev_event_work *switchdev_work = 30148c2ecf20Sopenharmony_ci container_of(work, struct mlxsw_sp_switchdev_event_work, work); 30158c2ecf20Sopenharmony_ci struct net_device *dev = switchdev_work->dev; 30168c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 30178c2ecf20Sopenharmony_ci struct net_device *br_dev; 30188c2ecf20Sopenharmony_ci 30198c2ecf20Sopenharmony_ci rtnl_lock(); 30208c2ecf20Sopenharmony_ci 30218c2ecf20Sopenharmony_ci if (!netif_running(dev)) 30228c2ecf20Sopenharmony_ci goto out; 30238c2ecf20Sopenharmony_ci br_dev = netdev_master_upper_dev_get(dev); 30248c2ecf20Sopenharmony_ci if (!br_dev) 30258c2ecf20Sopenharmony_ci goto out; 30268c2ecf20Sopenharmony_ci if (!netif_is_bridge_master(br_dev)) 30278c2ecf20Sopenharmony_ci goto out; 30288c2ecf20Sopenharmony_ci mlxsw_sp = mlxsw_sp_lower_get(br_dev); 30298c2ecf20Sopenharmony_ci if (!mlxsw_sp) 30308c2ecf20Sopenharmony_ci goto out; 30318c2ecf20Sopenharmony_ci 30328c2ecf20Sopenharmony_ci switch (switchdev_work->event) { 30338c2ecf20Sopenharmony_ci case SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE: 30348c2ecf20Sopenharmony_ci mlxsw_sp_switchdev_vxlan_fdb_add(mlxsw_sp, switchdev_work); 30358c2ecf20Sopenharmony_ci break; 30368c2ecf20Sopenharmony_ci case SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE: 30378c2ecf20Sopenharmony_ci mlxsw_sp_switchdev_vxlan_fdb_del(mlxsw_sp, switchdev_work); 30388c2ecf20Sopenharmony_ci break; 30398c2ecf20Sopenharmony_ci } 30408c2ecf20Sopenharmony_ci 30418c2ecf20Sopenharmony_ciout: 30428c2ecf20Sopenharmony_ci rtnl_unlock(); 30438c2ecf20Sopenharmony_ci kfree(switchdev_work); 30448c2ecf20Sopenharmony_ci dev_put(dev); 30458c2ecf20Sopenharmony_ci} 30468c2ecf20Sopenharmony_ci 30478c2ecf20Sopenharmony_cistatic int 30488c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_work_prepare(struct mlxsw_sp_switchdev_event_work * 30498c2ecf20Sopenharmony_ci switchdev_work, 30508c2ecf20Sopenharmony_ci struct switchdev_notifier_info *info) 30518c2ecf20Sopenharmony_ci{ 30528c2ecf20Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(switchdev_work->dev); 30538c2ecf20Sopenharmony_ci struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info; 30548c2ecf20Sopenharmony_ci struct vxlan_config *cfg = &vxlan->cfg; 30558c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack; 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_ci extack = switchdev_notifier_info_to_extack(info); 30588c2ecf20Sopenharmony_ci vxlan_fdb_info = container_of(info, 30598c2ecf20Sopenharmony_ci struct switchdev_notifier_vxlan_fdb_info, 30608c2ecf20Sopenharmony_ci info); 30618c2ecf20Sopenharmony_ci 30628c2ecf20Sopenharmony_ci if (vxlan_fdb_info->remote_port != cfg->dst_port) { 30638c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Non-default remote port is not supported"); 30648c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 30658c2ecf20Sopenharmony_ci } 30668c2ecf20Sopenharmony_ci if (vxlan_fdb_info->remote_vni != cfg->vni || 30678c2ecf20Sopenharmony_ci vxlan_fdb_info->vni != cfg->vni) { 30688c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Non-default VNI is not supported"); 30698c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 30708c2ecf20Sopenharmony_ci } 30718c2ecf20Sopenharmony_ci if (vxlan_fdb_info->remote_ifindex) { 30728c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Local interface is not supported"); 30738c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 30748c2ecf20Sopenharmony_ci } 30758c2ecf20Sopenharmony_ci if (is_multicast_ether_addr(vxlan_fdb_info->eth_addr)) { 30768c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Multicast MAC addresses not supported"); 30778c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 30788c2ecf20Sopenharmony_ci } 30798c2ecf20Sopenharmony_ci if (vxlan_addr_multicast(&vxlan_fdb_info->remote_ip)) { 30808c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Multicast destination IP is not supported"); 30818c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 30828c2ecf20Sopenharmony_ci } 30838c2ecf20Sopenharmony_ci 30848c2ecf20Sopenharmony_ci switchdev_work->vxlan_fdb_info = *vxlan_fdb_info; 30858c2ecf20Sopenharmony_ci 30868c2ecf20Sopenharmony_ci return 0; 30878c2ecf20Sopenharmony_ci} 30888c2ecf20Sopenharmony_ci 30898c2ecf20Sopenharmony_ci/* Called under rcu_read_lock() */ 30908c2ecf20Sopenharmony_cistatic int mlxsw_sp_switchdev_event(struct notifier_block *unused, 30918c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 30928c2ecf20Sopenharmony_ci{ 30938c2ecf20Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 30948c2ecf20Sopenharmony_ci struct mlxsw_sp_switchdev_event_work *switchdev_work; 30958c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info; 30968c2ecf20Sopenharmony_ci struct switchdev_notifier_info *info = ptr; 30978c2ecf20Sopenharmony_ci struct net_device *br_dev; 30988c2ecf20Sopenharmony_ci int err; 30998c2ecf20Sopenharmony_ci 31008c2ecf20Sopenharmony_ci if (event == SWITCHDEV_PORT_ATTR_SET) { 31018c2ecf20Sopenharmony_ci err = switchdev_handle_port_attr_set(dev, ptr, 31028c2ecf20Sopenharmony_ci mlxsw_sp_port_dev_check, 31038c2ecf20Sopenharmony_ci mlxsw_sp_port_attr_set); 31048c2ecf20Sopenharmony_ci return notifier_from_errno(err); 31058c2ecf20Sopenharmony_ci } 31068c2ecf20Sopenharmony_ci 31078c2ecf20Sopenharmony_ci /* Tunnel devices are not our uppers, so check their master instead */ 31088c2ecf20Sopenharmony_ci br_dev = netdev_master_upper_dev_get_rcu(dev); 31098c2ecf20Sopenharmony_ci if (!br_dev) 31108c2ecf20Sopenharmony_ci return NOTIFY_DONE; 31118c2ecf20Sopenharmony_ci if (!netif_is_bridge_master(br_dev)) 31128c2ecf20Sopenharmony_ci return NOTIFY_DONE; 31138c2ecf20Sopenharmony_ci if (!mlxsw_sp_port_dev_lower_find_rcu(br_dev)) 31148c2ecf20Sopenharmony_ci return NOTIFY_DONE; 31158c2ecf20Sopenharmony_ci 31168c2ecf20Sopenharmony_ci switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); 31178c2ecf20Sopenharmony_ci if (!switchdev_work) 31188c2ecf20Sopenharmony_ci return NOTIFY_BAD; 31198c2ecf20Sopenharmony_ci 31208c2ecf20Sopenharmony_ci switchdev_work->dev = dev; 31218c2ecf20Sopenharmony_ci switchdev_work->event = event; 31228c2ecf20Sopenharmony_ci 31238c2ecf20Sopenharmony_ci switch (event) { 31248c2ecf20Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 31258c2ecf20Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 31268c2ecf20Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_BRIDGE: 31278c2ecf20Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_BRIDGE: 31288c2ecf20Sopenharmony_ci fdb_info = container_of(info, 31298c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info, 31308c2ecf20Sopenharmony_ci info); 31318c2ecf20Sopenharmony_ci INIT_WORK(&switchdev_work->work, 31328c2ecf20Sopenharmony_ci mlxsw_sp_switchdev_bridge_fdb_event_work); 31338c2ecf20Sopenharmony_ci memcpy(&switchdev_work->fdb_info, ptr, 31348c2ecf20Sopenharmony_ci sizeof(switchdev_work->fdb_info)); 31358c2ecf20Sopenharmony_ci switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); 31368c2ecf20Sopenharmony_ci if (!switchdev_work->fdb_info.addr) 31378c2ecf20Sopenharmony_ci goto err_addr_alloc; 31388c2ecf20Sopenharmony_ci ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, 31398c2ecf20Sopenharmony_ci fdb_info->addr); 31408c2ecf20Sopenharmony_ci /* Take a reference on the device. This can be either 31418c2ecf20Sopenharmony_ci * upper device containig mlxsw_sp_port or just a 31428c2ecf20Sopenharmony_ci * mlxsw_sp_port 31438c2ecf20Sopenharmony_ci */ 31448c2ecf20Sopenharmony_ci dev_hold(dev); 31458c2ecf20Sopenharmony_ci break; 31468c2ecf20Sopenharmony_ci case SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE: 31478c2ecf20Sopenharmony_ci case SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE: 31488c2ecf20Sopenharmony_ci INIT_WORK(&switchdev_work->work, 31498c2ecf20Sopenharmony_ci mlxsw_sp_switchdev_vxlan_fdb_event_work); 31508c2ecf20Sopenharmony_ci err = mlxsw_sp_switchdev_vxlan_work_prepare(switchdev_work, 31518c2ecf20Sopenharmony_ci info); 31528c2ecf20Sopenharmony_ci if (err) 31538c2ecf20Sopenharmony_ci goto err_vxlan_work_prepare; 31548c2ecf20Sopenharmony_ci dev_hold(dev); 31558c2ecf20Sopenharmony_ci break; 31568c2ecf20Sopenharmony_ci default: 31578c2ecf20Sopenharmony_ci kfree(switchdev_work); 31588c2ecf20Sopenharmony_ci return NOTIFY_DONE; 31598c2ecf20Sopenharmony_ci } 31608c2ecf20Sopenharmony_ci 31618c2ecf20Sopenharmony_ci mlxsw_core_schedule_work(&switchdev_work->work); 31628c2ecf20Sopenharmony_ci 31638c2ecf20Sopenharmony_ci return NOTIFY_DONE; 31648c2ecf20Sopenharmony_ci 31658c2ecf20Sopenharmony_cierr_vxlan_work_prepare: 31668c2ecf20Sopenharmony_cierr_addr_alloc: 31678c2ecf20Sopenharmony_ci kfree(switchdev_work); 31688c2ecf20Sopenharmony_ci return NOTIFY_BAD; 31698c2ecf20Sopenharmony_ci} 31708c2ecf20Sopenharmony_ci 31718c2ecf20Sopenharmony_cistruct notifier_block mlxsw_sp_switchdev_notifier = { 31728c2ecf20Sopenharmony_ci .notifier_call = mlxsw_sp_switchdev_event, 31738c2ecf20Sopenharmony_ci}; 31748c2ecf20Sopenharmony_ci 31758c2ecf20Sopenharmony_cistatic int 31768c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp, 31778c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 31788c2ecf20Sopenharmony_ci const struct net_device *vxlan_dev, u16 vid, 31798c2ecf20Sopenharmony_ci bool flag_untagged, bool flag_pvid, 31808c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 31818c2ecf20Sopenharmony_ci{ 31828c2ecf20Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); 31838c2ecf20Sopenharmony_ci __be32 vni = vxlan->cfg.vni; 31848c2ecf20Sopenharmony_ci struct mlxsw_sp_fid *fid; 31858c2ecf20Sopenharmony_ci u16 old_vid; 31868c2ecf20Sopenharmony_ci int err; 31878c2ecf20Sopenharmony_ci 31888c2ecf20Sopenharmony_ci /* We cannot have the same VLAN as PVID and egress untagged on multiple 31898c2ecf20Sopenharmony_ci * VxLAN devices. Note that we get this notification before the VLAN is 31908c2ecf20Sopenharmony_ci * actually added to the bridge's database, so it is not possible for 31918c2ecf20Sopenharmony_ci * the lookup function to return 'vxlan_dev' 31928c2ecf20Sopenharmony_ci */ 31938c2ecf20Sopenharmony_ci if (flag_untagged && flag_pvid && 31948c2ecf20Sopenharmony_ci mlxsw_sp_bridge_8021q_vxlan_dev_find(bridge_device->dev, vid)) { 31958c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VLAN already mapped to a different VNI"); 31968c2ecf20Sopenharmony_ci return -EINVAL; 31978c2ecf20Sopenharmony_ci } 31988c2ecf20Sopenharmony_ci 31998c2ecf20Sopenharmony_ci if (!netif_running(vxlan_dev)) 32008c2ecf20Sopenharmony_ci return 0; 32018c2ecf20Sopenharmony_ci 32028c2ecf20Sopenharmony_ci /* First case: FID is not associated with this VNI, but the new VLAN 32038c2ecf20Sopenharmony_ci * is both PVID and egress untagged. Need to enable NVE on the FID, if 32048c2ecf20Sopenharmony_ci * it exists 32058c2ecf20Sopenharmony_ci */ 32068c2ecf20Sopenharmony_ci fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni); 32078c2ecf20Sopenharmony_ci if (!fid) { 32088c2ecf20Sopenharmony_ci if (!flag_untagged || !flag_pvid) 32098c2ecf20Sopenharmony_ci return 0; 32108c2ecf20Sopenharmony_ci return mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, 32118c2ecf20Sopenharmony_ci vxlan_dev, vid, extack); 32128c2ecf20Sopenharmony_ci } 32138c2ecf20Sopenharmony_ci 32148c2ecf20Sopenharmony_ci /* Second case: FID is associated with the VNI and the VLAN associated 32158c2ecf20Sopenharmony_ci * with the FID is the same as the notified VLAN. This means the flags 32168c2ecf20Sopenharmony_ci * (PVID / egress untagged) were toggled and that NVE should be 32178c2ecf20Sopenharmony_ci * disabled on the FID 32188c2ecf20Sopenharmony_ci */ 32198c2ecf20Sopenharmony_ci old_vid = mlxsw_sp_fid_8021q_vid(fid); 32208c2ecf20Sopenharmony_ci if (vid == old_vid) { 32218c2ecf20Sopenharmony_ci if (WARN_ON(flag_untagged && flag_pvid)) { 32228c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 32238c2ecf20Sopenharmony_ci return -EINVAL; 32248c2ecf20Sopenharmony_ci } 32258c2ecf20Sopenharmony_ci mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev); 32268c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 32278c2ecf20Sopenharmony_ci return 0; 32288c2ecf20Sopenharmony_ci } 32298c2ecf20Sopenharmony_ci 32308c2ecf20Sopenharmony_ci /* Third case: A new VLAN was configured on the VxLAN device, but this 32318c2ecf20Sopenharmony_ci * VLAN is not PVID, so there is nothing to do. 32328c2ecf20Sopenharmony_ci */ 32338c2ecf20Sopenharmony_ci if (!flag_pvid) { 32348c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 32358c2ecf20Sopenharmony_ci return 0; 32368c2ecf20Sopenharmony_ci } 32378c2ecf20Sopenharmony_ci 32388c2ecf20Sopenharmony_ci /* Fourth case: Thew new VLAN is PVID, which means the VLAN currently 32398c2ecf20Sopenharmony_ci * mapped to the VNI should be unmapped 32408c2ecf20Sopenharmony_ci */ 32418c2ecf20Sopenharmony_ci mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev); 32428c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 32438c2ecf20Sopenharmony_ci 32448c2ecf20Sopenharmony_ci /* Fifth case: The new VLAN is also egress untagged, which means the 32458c2ecf20Sopenharmony_ci * VLAN needs to be mapped to the VNI 32468c2ecf20Sopenharmony_ci */ 32478c2ecf20Sopenharmony_ci if (!flag_untagged) 32488c2ecf20Sopenharmony_ci return 0; 32498c2ecf20Sopenharmony_ci 32508c2ecf20Sopenharmony_ci err = mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, vid, 32518c2ecf20Sopenharmony_ci extack); 32528c2ecf20Sopenharmony_ci if (err) 32538c2ecf20Sopenharmony_ci goto err_vxlan_join; 32548c2ecf20Sopenharmony_ci 32558c2ecf20Sopenharmony_ci return 0; 32568c2ecf20Sopenharmony_ci 32578c2ecf20Sopenharmony_cierr_vxlan_join: 32588c2ecf20Sopenharmony_ci mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, old_vid, 32598c2ecf20Sopenharmony_ci NULL); 32608c2ecf20Sopenharmony_ci return err; 32618c2ecf20Sopenharmony_ci} 32628c2ecf20Sopenharmony_ci 32638c2ecf20Sopenharmony_cistatic void 32648c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlan_del(struct mlxsw_sp *mlxsw_sp, 32658c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 32668c2ecf20Sopenharmony_ci const struct net_device *vxlan_dev, u16 vid) 32678c2ecf20Sopenharmony_ci{ 32688c2ecf20Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); 32698c2ecf20Sopenharmony_ci __be32 vni = vxlan->cfg.vni; 32708c2ecf20Sopenharmony_ci struct mlxsw_sp_fid *fid; 32718c2ecf20Sopenharmony_ci 32728c2ecf20Sopenharmony_ci if (!netif_running(vxlan_dev)) 32738c2ecf20Sopenharmony_ci return; 32748c2ecf20Sopenharmony_ci 32758c2ecf20Sopenharmony_ci fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni); 32768c2ecf20Sopenharmony_ci if (!fid) 32778c2ecf20Sopenharmony_ci return; 32788c2ecf20Sopenharmony_ci 32798c2ecf20Sopenharmony_ci /* A different VLAN than the one mapped to the VNI is deleted */ 32808c2ecf20Sopenharmony_ci if (mlxsw_sp_fid_8021q_vid(fid) != vid) 32818c2ecf20Sopenharmony_ci goto out; 32828c2ecf20Sopenharmony_ci 32838c2ecf20Sopenharmony_ci mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev); 32848c2ecf20Sopenharmony_ci 32858c2ecf20Sopenharmony_ciout: 32868c2ecf20Sopenharmony_ci mlxsw_sp_fid_put(fid); 32878c2ecf20Sopenharmony_ci} 32888c2ecf20Sopenharmony_ci 32898c2ecf20Sopenharmony_cistatic int 32908c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev, 32918c2ecf20Sopenharmony_ci struct switchdev_notifier_port_obj_info * 32928c2ecf20Sopenharmony_ci port_obj_info) 32938c2ecf20Sopenharmony_ci{ 32948c2ecf20Sopenharmony_ci struct switchdev_obj_port_vlan *vlan = 32958c2ecf20Sopenharmony_ci SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj); 32968c2ecf20Sopenharmony_ci bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 32978c2ecf20Sopenharmony_ci bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; 32988c2ecf20Sopenharmony_ci struct switchdev_trans *trans = port_obj_info->trans; 32998c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 33008c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack; 33018c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 33028c2ecf20Sopenharmony_ci struct net_device *br_dev; 33038c2ecf20Sopenharmony_ci u16 vid; 33048c2ecf20Sopenharmony_ci 33058c2ecf20Sopenharmony_ci extack = switchdev_notifier_info_to_extack(&port_obj_info->info); 33068c2ecf20Sopenharmony_ci br_dev = netdev_master_upper_dev_get(vxlan_dev); 33078c2ecf20Sopenharmony_ci if (!br_dev) 33088c2ecf20Sopenharmony_ci return 0; 33098c2ecf20Sopenharmony_ci 33108c2ecf20Sopenharmony_ci mlxsw_sp = mlxsw_sp_lower_get(br_dev); 33118c2ecf20Sopenharmony_ci if (!mlxsw_sp) 33128c2ecf20Sopenharmony_ci return 0; 33138c2ecf20Sopenharmony_ci 33148c2ecf20Sopenharmony_ci port_obj_info->handled = true; 33158c2ecf20Sopenharmony_ci 33168c2ecf20Sopenharmony_ci if (switchdev_trans_ph_commit(trans)) 33178c2ecf20Sopenharmony_ci return 0; 33188c2ecf20Sopenharmony_ci 33198c2ecf20Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 33208c2ecf20Sopenharmony_ci if (!bridge_device) 33218c2ecf20Sopenharmony_ci return -EINVAL; 33228c2ecf20Sopenharmony_ci 33238c2ecf20Sopenharmony_ci if (!bridge_device->vlan_enabled) 33248c2ecf20Sopenharmony_ci return 0; 33258c2ecf20Sopenharmony_ci 33268c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 33278c2ecf20Sopenharmony_ci int err; 33288c2ecf20Sopenharmony_ci 33298c2ecf20Sopenharmony_ci err = mlxsw_sp_switchdev_vxlan_vlan_add(mlxsw_sp, bridge_device, 33308c2ecf20Sopenharmony_ci vxlan_dev, vid, 33318c2ecf20Sopenharmony_ci flag_untagged, 33328c2ecf20Sopenharmony_ci flag_pvid, extack); 33338c2ecf20Sopenharmony_ci if (err) 33348c2ecf20Sopenharmony_ci return err; 33358c2ecf20Sopenharmony_ci } 33368c2ecf20Sopenharmony_ci 33378c2ecf20Sopenharmony_ci return 0; 33388c2ecf20Sopenharmony_ci} 33398c2ecf20Sopenharmony_ci 33408c2ecf20Sopenharmony_cistatic void 33418c2ecf20Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlans_del(struct net_device *vxlan_dev, 33428c2ecf20Sopenharmony_ci struct switchdev_notifier_port_obj_info * 33438c2ecf20Sopenharmony_ci port_obj_info) 33448c2ecf20Sopenharmony_ci{ 33458c2ecf20Sopenharmony_ci struct switchdev_obj_port_vlan *vlan = 33468c2ecf20Sopenharmony_ci SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj); 33478c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 33488c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 33498c2ecf20Sopenharmony_ci struct net_device *br_dev; 33508c2ecf20Sopenharmony_ci u16 vid; 33518c2ecf20Sopenharmony_ci 33528c2ecf20Sopenharmony_ci br_dev = netdev_master_upper_dev_get(vxlan_dev); 33538c2ecf20Sopenharmony_ci if (!br_dev) 33548c2ecf20Sopenharmony_ci return; 33558c2ecf20Sopenharmony_ci 33568c2ecf20Sopenharmony_ci mlxsw_sp = mlxsw_sp_lower_get(br_dev); 33578c2ecf20Sopenharmony_ci if (!mlxsw_sp) 33588c2ecf20Sopenharmony_ci return; 33598c2ecf20Sopenharmony_ci 33608c2ecf20Sopenharmony_ci port_obj_info->handled = true; 33618c2ecf20Sopenharmony_ci 33628c2ecf20Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 33638c2ecf20Sopenharmony_ci if (!bridge_device) 33648c2ecf20Sopenharmony_ci return; 33658c2ecf20Sopenharmony_ci 33668c2ecf20Sopenharmony_ci if (!bridge_device->vlan_enabled) 33678c2ecf20Sopenharmony_ci return; 33688c2ecf20Sopenharmony_ci 33698c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) 33708c2ecf20Sopenharmony_ci mlxsw_sp_switchdev_vxlan_vlan_del(mlxsw_sp, bridge_device, 33718c2ecf20Sopenharmony_ci vxlan_dev, vid); 33728c2ecf20Sopenharmony_ci} 33738c2ecf20Sopenharmony_ci 33748c2ecf20Sopenharmony_cistatic int 33758c2ecf20Sopenharmony_cimlxsw_sp_switchdev_handle_vxlan_obj_add(struct net_device *vxlan_dev, 33768c2ecf20Sopenharmony_ci struct switchdev_notifier_port_obj_info * 33778c2ecf20Sopenharmony_ci port_obj_info) 33788c2ecf20Sopenharmony_ci{ 33798c2ecf20Sopenharmony_ci int err = 0; 33808c2ecf20Sopenharmony_ci 33818c2ecf20Sopenharmony_ci switch (port_obj_info->obj->id) { 33828c2ecf20Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 33838c2ecf20Sopenharmony_ci err = mlxsw_sp_switchdev_vxlan_vlans_add(vxlan_dev, 33848c2ecf20Sopenharmony_ci port_obj_info); 33858c2ecf20Sopenharmony_ci break; 33868c2ecf20Sopenharmony_ci default: 33878c2ecf20Sopenharmony_ci break; 33888c2ecf20Sopenharmony_ci } 33898c2ecf20Sopenharmony_ci 33908c2ecf20Sopenharmony_ci return err; 33918c2ecf20Sopenharmony_ci} 33928c2ecf20Sopenharmony_ci 33938c2ecf20Sopenharmony_cistatic void 33948c2ecf20Sopenharmony_cimlxsw_sp_switchdev_handle_vxlan_obj_del(struct net_device *vxlan_dev, 33958c2ecf20Sopenharmony_ci struct switchdev_notifier_port_obj_info * 33968c2ecf20Sopenharmony_ci port_obj_info) 33978c2ecf20Sopenharmony_ci{ 33988c2ecf20Sopenharmony_ci switch (port_obj_info->obj->id) { 33998c2ecf20Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 34008c2ecf20Sopenharmony_ci mlxsw_sp_switchdev_vxlan_vlans_del(vxlan_dev, port_obj_info); 34018c2ecf20Sopenharmony_ci break; 34028c2ecf20Sopenharmony_ci default: 34038c2ecf20Sopenharmony_ci break; 34048c2ecf20Sopenharmony_ci } 34058c2ecf20Sopenharmony_ci} 34068c2ecf20Sopenharmony_ci 34078c2ecf20Sopenharmony_cistatic int mlxsw_sp_switchdev_blocking_event(struct notifier_block *unused, 34088c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 34098c2ecf20Sopenharmony_ci{ 34108c2ecf20Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 34118c2ecf20Sopenharmony_ci int err = 0; 34128c2ecf20Sopenharmony_ci 34138c2ecf20Sopenharmony_ci switch (event) { 34148c2ecf20Sopenharmony_ci case SWITCHDEV_PORT_OBJ_ADD: 34158c2ecf20Sopenharmony_ci if (netif_is_vxlan(dev)) 34168c2ecf20Sopenharmony_ci err = mlxsw_sp_switchdev_handle_vxlan_obj_add(dev, ptr); 34178c2ecf20Sopenharmony_ci else 34188c2ecf20Sopenharmony_ci err = switchdev_handle_port_obj_add(dev, ptr, 34198c2ecf20Sopenharmony_ci mlxsw_sp_port_dev_check, 34208c2ecf20Sopenharmony_ci mlxsw_sp_port_obj_add); 34218c2ecf20Sopenharmony_ci return notifier_from_errno(err); 34228c2ecf20Sopenharmony_ci case SWITCHDEV_PORT_OBJ_DEL: 34238c2ecf20Sopenharmony_ci if (netif_is_vxlan(dev)) 34248c2ecf20Sopenharmony_ci mlxsw_sp_switchdev_handle_vxlan_obj_del(dev, ptr); 34258c2ecf20Sopenharmony_ci else 34268c2ecf20Sopenharmony_ci err = switchdev_handle_port_obj_del(dev, ptr, 34278c2ecf20Sopenharmony_ci mlxsw_sp_port_dev_check, 34288c2ecf20Sopenharmony_ci mlxsw_sp_port_obj_del); 34298c2ecf20Sopenharmony_ci return notifier_from_errno(err); 34308c2ecf20Sopenharmony_ci case SWITCHDEV_PORT_ATTR_SET: 34318c2ecf20Sopenharmony_ci err = switchdev_handle_port_attr_set(dev, ptr, 34328c2ecf20Sopenharmony_ci mlxsw_sp_port_dev_check, 34338c2ecf20Sopenharmony_ci mlxsw_sp_port_attr_set); 34348c2ecf20Sopenharmony_ci return notifier_from_errno(err); 34358c2ecf20Sopenharmony_ci } 34368c2ecf20Sopenharmony_ci 34378c2ecf20Sopenharmony_ci return NOTIFY_DONE; 34388c2ecf20Sopenharmony_ci} 34398c2ecf20Sopenharmony_ci 34408c2ecf20Sopenharmony_cistatic struct notifier_block mlxsw_sp_switchdev_blocking_notifier = { 34418c2ecf20Sopenharmony_ci .notifier_call = mlxsw_sp_switchdev_blocking_event, 34428c2ecf20Sopenharmony_ci}; 34438c2ecf20Sopenharmony_ci 34448c2ecf20Sopenharmony_ciu8 34458c2ecf20Sopenharmony_cimlxsw_sp_bridge_port_stp_state(struct mlxsw_sp_bridge_port *bridge_port) 34468c2ecf20Sopenharmony_ci{ 34478c2ecf20Sopenharmony_ci return bridge_port->stp_state; 34488c2ecf20Sopenharmony_ci} 34498c2ecf20Sopenharmony_ci 34508c2ecf20Sopenharmony_cistatic int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp) 34518c2ecf20Sopenharmony_ci{ 34528c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge; 34538c2ecf20Sopenharmony_ci struct notifier_block *nb; 34548c2ecf20Sopenharmony_ci int err; 34558c2ecf20Sopenharmony_ci 34568c2ecf20Sopenharmony_ci err = mlxsw_sp_ageing_set(mlxsw_sp, MLXSW_SP_DEFAULT_AGEING_TIME); 34578c2ecf20Sopenharmony_ci if (err) { 34588c2ecf20Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, "Failed to set default ageing time\n"); 34598c2ecf20Sopenharmony_ci return err; 34608c2ecf20Sopenharmony_ci } 34618c2ecf20Sopenharmony_ci 34628c2ecf20Sopenharmony_ci err = register_switchdev_notifier(&mlxsw_sp_switchdev_notifier); 34638c2ecf20Sopenharmony_ci if (err) { 34648c2ecf20Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, "Failed to register switchdev notifier\n"); 34658c2ecf20Sopenharmony_ci return err; 34668c2ecf20Sopenharmony_ci } 34678c2ecf20Sopenharmony_ci 34688c2ecf20Sopenharmony_ci nb = &mlxsw_sp_switchdev_blocking_notifier; 34698c2ecf20Sopenharmony_ci err = register_switchdev_blocking_notifier(nb); 34708c2ecf20Sopenharmony_ci if (err) { 34718c2ecf20Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, "Failed to register switchdev blocking notifier\n"); 34728c2ecf20Sopenharmony_ci goto err_register_switchdev_blocking_notifier; 34738c2ecf20Sopenharmony_ci } 34748c2ecf20Sopenharmony_ci 34758c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&bridge->fdb_notify.dw, mlxsw_sp_fdb_notify_work); 34768c2ecf20Sopenharmony_ci bridge->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL; 34778c2ecf20Sopenharmony_ci mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp, false); 34788c2ecf20Sopenharmony_ci return 0; 34798c2ecf20Sopenharmony_ci 34808c2ecf20Sopenharmony_cierr_register_switchdev_blocking_notifier: 34818c2ecf20Sopenharmony_ci unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier); 34828c2ecf20Sopenharmony_ci return err; 34838c2ecf20Sopenharmony_ci} 34848c2ecf20Sopenharmony_ci 34858c2ecf20Sopenharmony_cistatic void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp) 34868c2ecf20Sopenharmony_ci{ 34878c2ecf20Sopenharmony_ci struct notifier_block *nb; 34888c2ecf20Sopenharmony_ci 34898c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&mlxsw_sp->bridge->fdb_notify.dw); 34908c2ecf20Sopenharmony_ci 34918c2ecf20Sopenharmony_ci nb = &mlxsw_sp_switchdev_blocking_notifier; 34928c2ecf20Sopenharmony_ci unregister_switchdev_blocking_notifier(nb); 34938c2ecf20Sopenharmony_ci 34948c2ecf20Sopenharmony_ci unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier); 34958c2ecf20Sopenharmony_ci} 34968c2ecf20Sopenharmony_ci 34978c2ecf20Sopenharmony_ciint mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp) 34988c2ecf20Sopenharmony_ci{ 34998c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge *bridge; 35008c2ecf20Sopenharmony_ci 35018c2ecf20Sopenharmony_ci bridge = kzalloc(sizeof(*mlxsw_sp->bridge), GFP_KERNEL); 35028c2ecf20Sopenharmony_ci if (!bridge) 35038c2ecf20Sopenharmony_ci return -ENOMEM; 35048c2ecf20Sopenharmony_ci mlxsw_sp->bridge = bridge; 35058c2ecf20Sopenharmony_ci bridge->mlxsw_sp = mlxsw_sp; 35068c2ecf20Sopenharmony_ci 35078c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mlxsw_sp->bridge->bridges_list); 35088c2ecf20Sopenharmony_ci 35098c2ecf20Sopenharmony_ci bridge->bridge_8021q_ops = &mlxsw_sp_bridge_8021q_ops; 35108c2ecf20Sopenharmony_ci bridge->bridge_8021d_ops = &mlxsw_sp_bridge_8021d_ops; 35118c2ecf20Sopenharmony_ci 35128c2ecf20Sopenharmony_ci return mlxsw_sp_fdb_init(mlxsw_sp); 35138c2ecf20Sopenharmony_ci} 35148c2ecf20Sopenharmony_ci 35158c2ecf20Sopenharmony_civoid mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp) 35168c2ecf20Sopenharmony_ci{ 35178c2ecf20Sopenharmony_ci mlxsw_sp_fdb_fini(mlxsw_sp); 35188c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&mlxsw_sp->bridge->bridges_list)); 35198c2ecf20Sopenharmony_ci kfree(mlxsw_sp->bridge); 35208c2ecf20Sopenharmony_ci} 35218c2ecf20Sopenharmony_ci 3522