162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/kernel.h> 562306a36Sopenharmony_ci#include <linux/types.h> 662306a36Sopenharmony_ci#include <linux/netdevice.h> 762306a36Sopenharmony_ci#include <linux/etherdevice.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/skbuff.h> 1162306a36Sopenharmony_ci#include <linux/if_vlan.h> 1262306a36Sopenharmony_ci#include <linux/if_bridge.h> 1362306a36Sopenharmony_ci#include <linux/workqueue.h> 1462306a36Sopenharmony_ci#include <linux/jiffies.h> 1562306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1662306a36Sopenharmony_ci#include <linux/netlink.h> 1762306a36Sopenharmony_ci#include <net/switchdev.h> 1862306a36Sopenharmony_ci#include <net/vxlan.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "spectrum_span.h" 2162306a36Sopenharmony_ci#include "spectrum_switchdev.h" 2262306a36Sopenharmony_ci#include "spectrum.h" 2362306a36Sopenharmony_ci#include "core.h" 2462306a36Sopenharmony_ci#include "reg.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct mlxsw_sp_bridge_ops; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct mlxsw_sp_bridge { 2962306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 3062306a36Sopenharmony_ci struct { 3162306a36Sopenharmony_ci struct delayed_work dw; 3262306a36Sopenharmony_ci#define MLXSW_SP_DEFAULT_LEARNING_INTERVAL 100 3362306a36Sopenharmony_ci unsigned int interval; /* ms */ 3462306a36Sopenharmony_ci } fdb_notify; 3562306a36Sopenharmony_ci#define MLXSW_SP_MIN_AGEING_TIME 10 3662306a36Sopenharmony_ci#define MLXSW_SP_MAX_AGEING_TIME 1000000 3762306a36Sopenharmony_ci#define MLXSW_SP_DEFAULT_AGEING_TIME 300 3862306a36Sopenharmony_ci u32 ageing_time; 3962306a36Sopenharmony_ci bool vlan_enabled_exists; 4062306a36Sopenharmony_ci struct list_head bridges_list; 4162306a36Sopenharmony_ci DECLARE_BITMAP(mids_bitmap, MLXSW_SP_MID_MAX); 4262306a36Sopenharmony_ci const struct mlxsw_sp_bridge_ops *bridge_8021q_ops; 4362306a36Sopenharmony_ci const struct mlxsw_sp_bridge_ops *bridge_8021d_ops; 4462306a36Sopenharmony_ci const struct mlxsw_sp_bridge_ops *bridge_8021ad_ops; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct mlxsw_sp_bridge_device { 4862306a36Sopenharmony_ci struct net_device *dev; 4962306a36Sopenharmony_ci struct list_head list; 5062306a36Sopenharmony_ci struct list_head ports_list; 5162306a36Sopenharmony_ci struct list_head mdb_list; 5262306a36Sopenharmony_ci struct rhashtable mdb_ht; 5362306a36Sopenharmony_ci u8 vlan_enabled:1, 5462306a36Sopenharmony_ci multicast_enabled:1, 5562306a36Sopenharmony_ci mrouter:1; 5662306a36Sopenharmony_ci const struct mlxsw_sp_bridge_ops *ops; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct mlxsw_sp_bridge_port { 6062306a36Sopenharmony_ci struct net_device *dev; 6162306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 6262306a36Sopenharmony_ci struct list_head list; 6362306a36Sopenharmony_ci struct list_head vlans_list; 6462306a36Sopenharmony_ci unsigned int ref_count; 6562306a36Sopenharmony_ci u8 stp_state; 6662306a36Sopenharmony_ci unsigned long flags; 6762306a36Sopenharmony_ci bool mrouter; 6862306a36Sopenharmony_ci bool lagged; 6962306a36Sopenharmony_ci union { 7062306a36Sopenharmony_ci u16 lag_id; 7162306a36Sopenharmony_ci u16 system_port; 7262306a36Sopenharmony_ci }; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistruct mlxsw_sp_bridge_vlan { 7662306a36Sopenharmony_ci struct list_head list; 7762306a36Sopenharmony_ci struct list_head port_vlan_list; 7862306a36Sopenharmony_ci u16 vid; 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistruct mlxsw_sp_bridge_ops { 8262306a36Sopenharmony_ci int (*port_join)(struct mlxsw_sp_bridge_device *bridge_device, 8362306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 8462306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 8562306a36Sopenharmony_ci struct netlink_ext_ack *extack); 8662306a36Sopenharmony_ci void (*port_leave)(struct mlxsw_sp_bridge_device *bridge_device, 8762306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 8862306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port); 8962306a36Sopenharmony_ci int (*vxlan_join)(struct mlxsw_sp_bridge_device *bridge_device, 9062306a36Sopenharmony_ci const struct net_device *vxlan_dev, u16 vid, 9162306a36Sopenharmony_ci struct netlink_ext_ack *extack); 9262306a36Sopenharmony_ci struct mlxsw_sp_fid * 9362306a36Sopenharmony_ci (*fid_get)(struct mlxsw_sp_bridge_device *bridge_device, 9462306a36Sopenharmony_ci u16 vid, struct netlink_ext_ack *extack); 9562306a36Sopenharmony_ci struct mlxsw_sp_fid * 9662306a36Sopenharmony_ci (*fid_lookup)(struct mlxsw_sp_bridge_device *bridge_device, 9762306a36Sopenharmony_ci u16 vid); 9862306a36Sopenharmony_ci u16 (*fid_vid)(struct mlxsw_sp_bridge_device *bridge_device, 9962306a36Sopenharmony_ci const struct mlxsw_sp_fid *fid); 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct mlxsw_sp_switchdev_ops { 10362306a36Sopenharmony_ci void (*init)(struct mlxsw_sp *mlxsw_sp); 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistruct mlxsw_sp_mdb_entry_key { 10762306a36Sopenharmony_ci unsigned char addr[ETH_ALEN]; 10862306a36Sopenharmony_ci u16 fid; 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistruct mlxsw_sp_mdb_entry { 11262306a36Sopenharmony_ci struct list_head list; 11362306a36Sopenharmony_ci struct rhash_head ht_node; 11462306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry_key key; 11562306a36Sopenharmony_ci u16 mid; 11662306a36Sopenharmony_ci struct list_head ports_list; 11762306a36Sopenharmony_ci u16 ports_count; 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistruct mlxsw_sp_mdb_entry_port { 12162306a36Sopenharmony_ci struct list_head list; /* Member of 'ports_list'. */ 12262306a36Sopenharmony_ci u16 local_port; 12362306a36Sopenharmony_ci refcount_t refcount; 12462306a36Sopenharmony_ci bool mrouter; 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp_mdb_ht_params = { 12862306a36Sopenharmony_ci .key_offset = offsetof(struct mlxsw_sp_mdb_entry, key), 12962306a36Sopenharmony_ci .head_offset = offsetof(struct mlxsw_sp_mdb_entry, ht_node), 13062306a36Sopenharmony_ci .key_len = sizeof(struct mlxsw_sp_mdb_entry_key), 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int 13462306a36Sopenharmony_cimlxsw_sp_bridge_port_fdb_flush(struct mlxsw_sp *mlxsw_sp, 13562306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 13662306a36Sopenharmony_ci u16 fid_index); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void 13962306a36Sopenharmony_cimlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, 14062306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 14162306a36Sopenharmony_ci u16 fid_index); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int 14462306a36Sopenharmony_cimlxsw_sp_bridge_mdb_mc_enable_sync(struct mlxsw_sp *mlxsw_sp, 14562306a36Sopenharmony_ci struct mlxsw_sp_bridge_device 14662306a36Sopenharmony_ci *bridge_device, bool mc_enabled); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void 14962306a36Sopenharmony_cimlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port, 15062306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 15162306a36Sopenharmony_ci bool add); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_device * 15462306a36Sopenharmony_cimlxsw_sp_bridge_device_find(const struct mlxsw_sp_bridge *bridge, 15562306a36Sopenharmony_ci const struct net_device *br_dev) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci list_for_each_entry(bridge_device, &bridge->bridges_list, list) 16062306a36Sopenharmony_ci if (bridge_device->dev == br_dev) 16162306a36Sopenharmony_ci return bridge_device; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return NULL; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cibool mlxsw_sp_bridge_device_is_offloaded(const struct mlxsw_sp *mlxsw_sp, 16762306a36Sopenharmony_ci const struct net_device *br_dev) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci return !!mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int mlxsw_sp_bridge_device_upper_rif_destroy(struct net_device *dev, 17362306a36Sopenharmony_ci struct netdev_nested_priv *priv) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = priv->data; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci mlxsw_sp_rif_destroy_by_dev(mlxsw_sp, dev); 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic void mlxsw_sp_bridge_device_rifs_destroy(struct mlxsw_sp *mlxsw_sp, 18262306a36Sopenharmony_ci struct net_device *dev) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct netdev_nested_priv priv = { 18562306a36Sopenharmony_ci .data = (void *)mlxsw_sp, 18662306a36Sopenharmony_ci }; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci mlxsw_sp_rif_destroy_by_dev(mlxsw_sp, dev); 18962306a36Sopenharmony_ci netdev_walk_all_upper_dev_rcu(dev, 19062306a36Sopenharmony_ci mlxsw_sp_bridge_device_upper_rif_destroy, 19162306a36Sopenharmony_ci &priv); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int mlxsw_sp_bridge_device_vxlan_init(struct mlxsw_sp_bridge *bridge, 19562306a36Sopenharmony_ci struct net_device *br_dev, 19662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct net_device *dev, *stop_dev; 19962306a36Sopenharmony_ci struct list_head *iter; 20062306a36Sopenharmony_ci int err; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci netdev_for_each_lower_dev(br_dev, dev, iter) { 20362306a36Sopenharmony_ci if (netif_is_vxlan(dev) && netif_running(dev)) { 20462306a36Sopenharmony_ci err = mlxsw_sp_bridge_vxlan_join(bridge->mlxsw_sp, 20562306a36Sopenharmony_ci br_dev, dev, 0, 20662306a36Sopenharmony_ci extack); 20762306a36Sopenharmony_ci if (err) { 20862306a36Sopenharmony_ci stop_dev = dev; 20962306a36Sopenharmony_ci goto err_vxlan_join; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cierr_vxlan_join: 21762306a36Sopenharmony_ci netdev_for_each_lower_dev(br_dev, dev, iter) { 21862306a36Sopenharmony_ci if (netif_is_vxlan(dev) && netif_running(dev)) { 21962306a36Sopenharmony_ci if (stop_dev == dev) 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci mlxsw_sp_bridge_vxlan_leave(bridge->mlxsw_sp, dev); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci return err; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void mlxsw_sp_bridge_device_vxlan_fini(struct mlxsw_sp_bridge *bridge, 22862306a36Sopenharmony_ci struct net_device *br_dev) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct net_device *dev; 23162306a36Sopenharmony_ci struct list_head *iter; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci netdev_for_each_lower_dev(br_dev, dev, iter) { 23462306a36Sopenharmony_ci if (netif_is_vxlan(dev) && netif_running(dev)) 23562306a36Sopenharmony_ci mlxsw_sp_bridge_vxlan_leave(bridge->mlxsw_sp, dev); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void mlxsw_sp_fdb_notify_work_schedule(struct mlxsw_sp *mlxsw_sp, 24062306a36Sopenharmony_ci bool no_delay) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge; 24362306a36Sopenharmony_ci unsigned int interval = no_delay ? 0 : bridge->fdb_notify.interval; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci mlxsw_core_schedule_dw(&bridge->fdb_notify.dw, 24662306a36Sopenharmony_ci msecs_to_jiffies(interval)); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_device * 25062306a36Sopenharmony_cimlxsw_sp_bridge_device_create(struct mlxsw_sp_bridge *bridge, 25162306a36Sopenharmony_ci struct net_device *br_dev, 25262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct device *dev = bridge->mlxsw_sp->bus_info->dev; 25562306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 25662306a36Sopenharmony_ci bool vlan_enabled = br_vlan_enabled(br_dev); 25762306a36Sopenharmony_ci int err; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (vlan_enabled && bridge->vlan_enabled_exists) { 26062306a36Sopenharmony_ci dev_err(dev, "Only one VLAN-aware bridge is supported\n"); 26162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Only one VLAN-aware bridge is supported"); 26262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci bridge_device = kzalloc(sizeof(*bridge_device), GFP_KERNEL); 26662306a36Sopenharmony_ci if (!bridge_device) 26762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci err = rhashtable_init(&bridge_device->mdb_ht, &mlxsw_sp_mdb_ht_params); 27062306a36Sopenharmony_ci if (err) 27162306a36Sopenharmony_ci goto err_mdb_rhashtable_init; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci bridge_device->dev = br_dev; 27462306a36Sopenharmony_ci bridge_device->vlan_enabled = vlan_enabled; 27562306a36Sopenharmony_ci bridge_device->multicast_enabled = br_multicast_enabled(br_dev); 27662306a36Sopenharmony_ci bridge_device->mrouter = br_multicast_router(br_dev); 27762306a36Sopenharmony_ci INIT_LIST_HEAD(&bridge_device->ports_list); 27862306a36Sopenharmony_ci if (vlan_enabled) { 27962306a36Sopenharmony_ci u16 proto; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci bridge->vlan_enabled_exists = true; 28262306a36Sopenharmony_ci br_vlan_get_proto(br_dev, &proto); 28362306a36Sopenharmony_ci if (proto == ETH_P_8021AD) 28462306a36Sopenharmony_ci bridge_device->ops = bridge->bridge_8021ad_ops; 28562306a36Sopenharmony_ci else 28662306a36Sopenharmony_ci bridge_device->ops = bridge->bridge_8021q_ops; 28762306a36Sopenharmony_ci } else { 28862306a36Sopenharmony_ci bridge_device->ops = bridge->bridge_8021d_ops; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci INIT_LIST_HEAD(&bridge_device->mdb_list); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (list_empty(&bridge->bridges_list)) 29362306a36Sopenharmony_ci mlxsw_sp_fdb_notify_work_schedule(bridge->mlxsw_sp, false); 29462306a36Sopenharmony_ci list_add(&bridge_device->list, &bridge->bridges_list); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* It is possible we already have VXLAN devices enslaved to the bridge. 29762306a36Sopenharmony_ci * In which case, we need to replay their configuration as if they were 29862306a36Sopenharmony_ci * just now enslaved to the bridge. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_ci err = mlxsw_sp_bridge_device_vxlan_init(bridge, br_dev, extack); 30162306a36Sopenharmony_ci if (err) 30262306a36Sopenharmony_ci goto err_vxlan_init; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return bridge_device; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cierr_vxlan_init: 30762306a36Sopenharmony_ci list_del(&bridge_device->list); 30862306a36Sopenharmony_ci if (bridge_device->vlan_enabled) 30962306a36Sopenharmony_ci bridge->vlan_enabled_exists = false; 31062306a36Sopenharmony_ci rhashtable_destroy(&bridge_device->mdb_ht); 31162306a36Sopenharmony_cierr_mdb_rhashtable_init: 31262306a36Sopenharmony_ci kfree(bridge_device); 31362306a36Sopenharmony_ci return ERR_PTR(err); 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic void 31762306a36Sopenharmony_cimlxsw_sp_bridge_device_destroy(struct mlxsw_sp_bridge *bridge, 31862306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci mlxsw_sp_bridge_device_vxlan_fini(bridge, bridge_device->dev); 32162306a36Sopenharmony_ci mlxsw_sp_bridge_device_rifs_destroy(bridge->mlxsw_sp, 32262306a36Sopenharmony_ci bridge_device->dev); 32362306a36Sopenharmony_ci list_del(&bridge_device->list); 32462306a36Sopenharmony_ci if (list_empty(&bridge->bridges_list)) 32562306a36Sopenharmony_ci cancel_delayed_work(&bridge->fdb_notify.dw); 32662306a36Sopenharmony_ci if (bridge_device->vlan_enabled) 32762306a36Sopenharmony_ci bridge->vlan_enabled_exists = false; 32862306a36Sopenharmony_ci WARN_ON(!list_empty(&bridge_device->ports_list)); 32962306a36Sopenharmony_ci WARN_ON(!list_empty(&bridge_device->mdb_list)); 33062306a36Sopenharmony_ci rhashtable_destroy(&bridge_device->mdb_ht); 33162306a36Sopenharmony_ci kfree(bridge_device); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_device * 33562306a36Sopenharmony_cimlxsw_sp_bridge_device_get(struct mlxsw_sp_bridge *bridge, 33662306a36Sopenharmony_ci struct net_device *br_dev, 33762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(bridge, br_dev); 34262306a36Sopenharmony_ci if (bridge_device) 34362306a36Sopenharmony_ci return bridge_device; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return mlxsw_sp_bridge_device_create(bridge, br_dev, extack); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic void 34962306a36Sopenharmony_cimlxsw_sp_bridge_device_put(struct mlxsw_sp_bridge *bridge, 35062306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci if (list_empty(&bridge_device->ports_list)) 35362306a36Sopenharmony_ci mlxsw_sp_bridge_device_destroy(bridge, bridge_device); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_port * 35762306a36Sopenharmony_ci__mlxsw_sp_bridge_port_find(const struct mlxsw_sp_bridge_device *bridge_device, 35862306a36Sopenharmony_ci const struct net_device *brport_dev) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci list_for_each_entry(bridge_port, &bridge_device->ports_list, list) { 36362306a36Sopenharmony_ci if (bridge_port->dev == brport_dev) 36462306a36Sopenharmony_ci return bridge_port; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return NULL; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistruct mlxsw_sp_bridge_port * 37162306a36Sopenharmony_cimlxsw_sp_bridge_port_find(struct mlxsw_sp_bridge *bridge, 37262306a36Sopenharmony_ci struct net_device *brport_dev) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct net_device *br_dev = netdev_master_upper_dev_get(brport_dev); 37562306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (!br_dev) 37862306a36Sopenharmony_ci return NULL; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(bridge, br_dev); 38162306a36Sopenharmony_ci if (!bridge_device) 38262306a36Sopenharmony_ci return NULL; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return __mlxsw_sp_bridge_port_find(bridge_device, brport_dev); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic int mlxsw_sp_port_obj_add(struct net_device *dev, const void *ctx, 38862306a36Sopenharmony_ci const struct switchdev_obj *obj, 38962306a36Sopenharmony_ci struct netlink_ext_ack *extack); 39062306a36Sopenharmony_cistatic int mlxsw_sp_port_obj_del(struct net_device *dev, const void *ctx, 39162306a36Sopenharmony_ci const struct switchdev_obj *obj); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistruct mlxsw_sp_bridge_port_replay_switchdev_objs { 39462306a36Sopenharmony_ci struct net_device *brport_dev; 39562306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 39662306a36Sopenharmony_ci int done; 39762306a36Sopenharmony_ci}; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int 40062306a36Sopenharmony_cimlxsw_sp_bridge_port_replay_switchdev_objs(struct notifier_block *nb, 40162306a36Sopenharmony_ci unsigned long event, void *ptr) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 40462306a36Sopenharmony_ci struct switchdev_notifier_port_obj_info *port_obj_info = ptr; 40562306a36Sopenharmony_ci struct netlink_ext_ack *extack = port_obj_info->info.extack; 40662306a36Sopenharmony_ci struct mlxsw_sp_bridge_port_replay_switchdev_objs *rso; 40762306a36Sopenharmony_ci int err = 0; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci rso = (void *)port_obj_info->info.ctx; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (event != SWITCHDEV_PORT_OBJ_ADD || 41262306a36Sopenharmony_ci dev != rso->brport_dev) 41362306a36Sopenharmony_ci goto out; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* When a port is joining the bridge through a LAG, there likely are 41662306a36Sopenharmony_ci * VLANs configured on that LAG already. The replay will thus attempt to 41762306a36Sopenharmony_ci * have the given port-vlans join the corresponding FIDs. But the LAG 41862306a36Sopenharmony_ci * netdevice has already called the ndo_vlan_rx_add_vid NDO for its VLAN 41962306a36Sopenharmony_ci * memberships, back before CHANGEUPPER was distributed and netdevice 42062306a36Sopenharmony_ci * master set. So now before propagating the VLAN events further, we 42162306a36Sopenharmony_ci * first need to kill the corresponding VID at the mlxsw_sp_port. 42262306a36Sopenharmony_ci * 42362306a36Sopenharmony_ci * Note that this doesn't need to be rolled back on failure -- if the 42462306a36Sopenharmony_ci * replay fails, the enslavement is off, and the VIDs would be killed by 42562306a36Sopenharmony_ci * LAG anyway as part of its rollback. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci if (port_obj_info->obj->id == SWITCHDEV_OBJ_ID_PORT_VLAN) { 42862306a36Sopenharmony_ci u16 vid = SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj)->vid; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci err = mlxsw_sp_port_kill_vid(rso->mlxsw_sp_port->dev, 0, vid); 43162306a36Sopenharmony_ci if (err) 43262306a36Sopenharmony_ci goto out; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci ++rso->done; 43662306a36Sopenharmony_ci err = mlxsw_sp_port_obj_add(rso->mlxsw_sp_port->dev, NULL, 43762306a36Sopenharmony_ci port_obj_info->obj, extack); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ciout: 44062306a36Sopenharmony_ci return notifier_from_errno(err); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic struct notifier_block mlxsw_sp_bridge_port_replay_switchdev_objs_nb = { 44462306a36Sopenharmony_ci .notifier_call = mlxsw_sp_bridge_port_replay_switchdev_objs, 44562306a36Sopenharmony_ci}; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic int 44862306a36Sopenharmony_cimlxsw_sp_bridge_port_unreplay_switchdev_objs(struct notifier_block *nb, 44962306a36Sopenharmony_ci unsigned long event, void *ptr) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 45262306a36Sopenharmony_ci struct switchdev_notifier_port_obj_info *port_obj_info = ptr; 45362306a36Sopenharmony_ci struct mlxsw_sp_bridge_port_replay_switchdev_objs *rso; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci rso = (void *)port_obj_info->info.ctx; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (event != SWITCHDEV_PORT_OBJ_ADD || 45862306a36Sopenharmony_ci dev != rso->brport_dev) 45962306a36Sopenharmony_ci return NOTIFY_DONE; 46062306a36Sopenharmony_ci if (!rso->done--) 46162306a36Sopenharmony_ci return NOTIFY_STOP; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci mlxsw_sp_port_obj_del(rso->mlxsw_sp_port->dev, NULL, 46462306a36Sopenharmony_ci port_obj_info->obj); 46562306a36Sopenharmony_ci return NOTIFY_DONE; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic struct notifier_block mlxsw_sp_bridge_port_unreplay_switchdev_objs_nb = { 46962306a36Sopenharmony_ci .notifier_call = mlxsw_sp_bridge_port_unreplay_switchdev_objs, 47062306a36Sopenharmony_ci}; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_port * 47362306a36Sopenharmony_cimlxsw_sp_bridge_port_create(struct mlxsw_sp_bridge_device *bridge_device, 47462306a36Sopenharmony_ci struct net_device *brport_dev, 47562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 47862306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 47962306a36Sopenharmony_ci int err; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci bridge_port = kzalloc(sizeof(*bridge_port), GFP_KERNEL); 48262306a36Sopenharmony_ci if (!bridge_port) 48362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(brport_dev); 48662306a36Sopenharmony_ci bridge_port->lagged = mlxsw_sp_port->lagged; 48762306a36Sopenharmony_ci if (bridge_port->lagged) 48862306a36Sopenharmony_ci bridge_port->lag_id = mlxsw_sp_port->lag_id; 48962306a36Sopenharmony_ci else 49062306a36Sopenharmony_ci bridge_port->system_port = mlxsw_sp_port->local_port; 49162306a36Sopenharmony_ci bridge_port->dev = brport_dev; 49262306a36Sopenharmony_ci bridge_port->bridge_device = bridge_device; 49362306a36Sopenharmony_ci bridge_port->stp_state = br_port_get_stp_state(brport_dev); 49462306a36Sopenharmony_ci bridge_port->flags = BR_LEARNING | BR_FLOOD | BR_LEARNING_SYNC | 49562306a36Sopenharmony_ci BR_MCAST_FLOOD; 49662306a36Sopenharmony_ci INIT_LIST_HEAD(&bridge_port->vlans_list); 49762306a36Sopenharmony_ci list_add(&bridge_port->list, &bridge_device->ports_list); 49862306a36Sopenharmony_ci bridge_port->ref_count = 1; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci err = switchdev_bridge_port_offload(brport_dev, mlxsw_sp_port->dev, 50162306a36Sopenharmony_ci NULL, NULL, NULL, false, extack); 50262306a36Sopenharmony_ci if (err) 50362306a36Sopenharmony_ci goto err_switchdev_offload; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci return bridge_port; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cierr_switchdev_offload: 50862306a36Sopenharmony_ci list_del(&bridge_port->list); 50962306a36Sopenharmony_ci kfree(bridge_port); 51062306a36Sopenharmony_ci return ERR_PTR(err); 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic void 51462306a36Sopenharmony_cimlxsw_sp_bridge_port_destroy(struct mlxsw_sp_bridge_port *bridge_port) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci switchdev_bridge_port_unoffload(bridge_port->dev, NULL, NULL, NULL); 51762306a36Sopenharmony_ci list_del(&bridge_port->list); 51862306a36Sopenharmony_ci WARN_ON(!list_empty(&bridge_port->vlans_list)); 51962306a36Sopenharmony_ci kfree(bridge_port); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_port * 52362306a36Sopenharmony_cimlxsw_sp_bridge_port_get(struct mlxsw_sp_bridge *bridge, 52462306a36Sopenharmony_ci struct net_device *brport_dev, 52562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct net_device *br_dev = netdev_master_upper_dev_get(brport_dev); 52862306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 52962306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 53062306a36Sopenharmony_ci int err; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(bridge, brport_dev); 53362306a36Sopenharmony_ci if (bridge_port) { 53462306a36Sopenharmony_ci bridge_port->ref_count++; 53562306a36Sopenharmony_ci return bridge_port; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_get(bridge, br_dev, extack); 53962306a36Sopenharmony_ci if (IS_ERR(bridge_device)) 54062306a36Sopenharmony_ci return ERR_CAST(bridge_device); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_create(bridge_device, brport_dev, 54362306a36Sopenharmony_ci extack); 54462306a36Sopenharmony_ci if (IS_ERR(bridge_port)) { 54562306a36Sopenharmony_ci err = PTR_ERR(bridge_port); 54662306a36Sopenharmony_ci goto err_bridge_port_create; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return bridge_port; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cierr_bridge_port_create: 55262306a36Sopenharmony_ci mlxsw_sp_bridge_device_put(bridge, bridge_device); 55362306a36Sopenharmony_ci return ERR_PTR(err); 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic void mlxsw_sp_bridge_port_put(struct mlxsw_sp_bridge *bridge, 55762306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (--bridge_port->ref_count != 0) 56262306a36Sopenharmony_ci return; 56362306a36Sopenharmony_ci bridge_device = bridge_port->bridge_device; 56462306a36Sopenharmony_ci mlxsw_sp_bridge_port_destroy(bridge_port); 56562306a36Sopenharmony_ci mlxsw_sp_bridge_device_put(bridge, bridge_device); 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic struct mlxsw_sp_port_vlan * 56962306a36Sopenharmony_cimlxsw_sp_port_vlan_find_by_bridge(struct mlxsw_sp_port *mlxsw_sp_port, 57062306a36Sopenharmony_ci const struct mlxsw_sp_bridge_device * 57162306a36Sopenharmony_ci bridge_device, 57262306a36Sopenharmony_ci u16 vid) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list, 57762306a36Sopenharmony_ci list) { 57862306a36Sopenharmony_ci if (!mlxsw_sp_port_vlan->bridge_port) 57962306a36Sopenharmony_ci continue; 58062306a36Sopenharmony_ci if (mlxsw_sp_port_vlan->bridge_port->bridge_device != 58162306a36Sopenharmony_ci bridge_device) 58262306a36Sopenharmony_ci continue; 58362306a36Sopenharmony_ci if (bridge_device->vlan_enabled && 58462306a36Sopenharmony_ci mlxsw_sp_port_vlan->vid != vid) 58562306a36Sopenharmony_ci continue; 58662306a36Sopenharmony_ci return mlxsw_sp_port_vlan; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci return NULL; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic struct mlxsw_sp_port_vlan* 59362306a36Sopenharmony_cimlxsw_sp_port_vlan_find_by_fid(struct mlxsw_sp_port *mlxsw_sp_port, 59462306a36Sopenharmony_ci u16 fid_index) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list, 59962306a36Sopenharmony_ci list) { 60062306a36Sopenharmony_ci struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (fid && mlxsw_sp_fid_index(fid) == fid_index) 60362306a36Sopenharmony_ci return mlxsw_sp_port_vlan; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return NULL; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_vlan * 61062306a36Sopenharmony_cimlxsw_sp_bridge_vlan_find(const struct mlxsw_sp_bridge_port *bridge_port, 61162306a36Sopenharmony_ci u16 vid) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) { 61662306a36Sopenharmony_ci if (bridge_vlan->vid == vid) 61762306a36Sopenharmony_ci return bridge_vlan; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return NULL; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_vlan * 62462306a36Sopenharmony_cimlxsw_sp_bridge_vlan_create(struct mlxsw_sp_bridge_port *bridge_port, u16 vid) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci bridge_vlan = kzalloc(sizeof(*bridge_vlan), GFP_KERNEL); 62962306a36Sopenharmony_ci if (!bridge_vlan) 63062306a36Sopenharmony_ci return NULL; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci INIT_LIST_HEAD(&bridge_vlan->port_vlan_list); 63362306a36Sopenharmony_ci bridge_vlan->vid = vid; 63462306a36Sopenharmony_ci list_add(&bridge_vlan->list, &bridge_port->vlans_list); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci return bridge_vlan; 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic void 64062306a36Sopenharmony_cimlxsw_sp_bridge_vlan_destroy(struct mlxsw_sp_bridge_vlan *bridge_vlan) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci list_del(&bridge_vlan->list); 64362306a36Sopenharmony_ci WARN_ON(!list_empty(&bridge_vlan->port_vlan_list)); 64462306a36Sopenharmony_ci kfree(bridge_vlan); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic struct mlxsw_sp_bridge_vlan * 64862306a36Sopenharmony_cimlxsw_sp_bridge_vlan_get(struct mlxsw_sp_bridge_port *bridge_port, u16 vid) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci bridge_vlan = mlxsw_sp_bridge_vlan_find(bridge_port, vid); 65362306a36Sopenharmony_ci if (bridge_vlan) 65462306a36Sopenharmony_ci return bridge_vlan; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci return mlxsw_sp_bridge_vlan_create(bridge_port, vid); 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic void mlxsw_sp_bridge_vlan_put(struct mlxsw_sp_bridge_vlan *bridge_vlan) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci if (list_empty(&bridge_vlan->port_vlan_list)) 66262306a36Sopenharmony_ci mlxsw_sp_bridge_vlan_destroy(bridge_vlan); 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic int 66662306a36Sopenharmony_cimlxsw_sp_port_bridge_vlan_stp_set(struct mlxsw_sp_port *mlxsw_sp_port, 66762306a36Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan, 66862306a36Sopenharmony_ci u8 state) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci list_for_each_entry(mlxsw_sp_port_vlan, &bridge_vlan->port_vlan_list, 67362306a36Sopenharmony_ci bridge_vlan_node) { 67462306a36Sopenharmony_ci if (mlxsw_sp_port_vlan->mlxsw_sp_port != mlxsw_sp_port) 67562306a36Sopenharmony_ci continue; 67662306a36Sopenharmony_ci return mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, 67762306a36Sopenharmony_ci bridge_vlan->vid, state); 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci return 0; 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port, 68462306a36Sopenharmony_ci struct net_device *orig_dev, 68562306a36Sopenharmony_ci u8 state) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 68862306a36Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 68962306a36Sopenharmony_ci int err; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* It's possible we failed to enslave the port, yet this 69262306a36Sopenharmony_ci * operation is executed due to it being deferred. 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge, 69562306a36Sopenharmony_ci orig_dev); 69662306a36Sopenharmony_ci if (!bridge_port) 69762306a36Sopenharmony_ci return 0; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) { 70062306a36Sopenharmony_ci err = mlxsw_sp_port_bridge_vlan_stp_set(mlxsw_sp_port, 70162306a36Sopenharmony_ci bridge_vlan, state); 70262306a36Sopenharmony_ci if (err) 70362306a36Sopenharmony_ci goto err_port_bridge_vlan_stp_set; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci bridge_port->stp_state = state; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci return 0; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cierr_port_bridge_vlan_stp_set: 71162306a36Sopenharmony_ci list_for_each_entry_continue_reverse(bridge_vlan, 71262306a36Sopenharmony_ci &bridge_port->vlans_list, list) 71362306a36Sopenharmony_ci mlxsw_sp_port_bridge_vlan_stp_set(mlxsw_sp_port, bridge_vlan, 71462306a36Sopenharmony_ci bridge_port->stp_state); 71562306a36Sopenharmony_ci return err; 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic int 71962306a36Sopenharmony_cimlxsw_sp_port_bridge_vlan_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, 72062306a36Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan, 72162306a36Sopenharmony_ci enum mlxsw_sp_flood_type packet_type, 72262306a36Sopenharmony_ci bool member) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci list_for_each_entry(mlxsw_sp_port_vlan, &bridge_vlan->port_vlan_list, 72762306a36Sopenharmony_ci bridge_vlan_node) { 72862306a36Sopenharmony_ci if (mlxsw_sp_port_vlan->mlxsw_sp_port != mlxsw_sp_port) 72962306a36Sopenharmony_ci continue; 73062306a36Sopenharmony_ci return mlxsw_sp_fid_flood_set(mlxsw_sp_port_vlan->fid, 73162306a36Sopenharmony_ci packet_type, 73262306a36Sopenharmony_ci mlxsw_sp_port->local_port, 73362306a36Sopenharmony_ci member); 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci return 0; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic int 74062306a36Sopenharmony_cimlxsw_sp_bridge_port_flood_table_set(struct mlxsw_sp_port *mlxsw_sp_port, 74162306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 74262306a36Sopenharmony_ci enum mlxsw_sp_flood_type packet_type, 74362306a36Sopenharmony_ci bool member) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 74662306a36Sopenharmony_ci int err; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) { 74962306a36Sopenharmony_ci err = mlxsw_sp_port_bridge_vlan_flood_set(mlxsw_sp_port, 75062306a36Sopenharmony_ci bridge_vlan, 75162306a36Sopenharmony_ci packet_type, 75262306a36Sopenharmony_ci member); 75362306a36Sopenharmony_ci if (err) 75462306a36Sopenharmony_ci goto err_port_bridge_vlan_flood_set; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci return 0; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cierr_port_bridge_vlan_flood_set: 76062306a36Sopenharmony_ci list_for_each_entry_continue_reverse(bridge_vlan, 76162306a36Sopenharmony_ci &bridge_port->vlans_list, list) 76262306a36Sopenharmony_ci mlxsw_sp_port_bridge_vlan_flood_set(mlxsw_sp_port, bridge_vlan, 76362306a36Sopenharmony_ci packet_type, !member); 76462306a36Sopenharmony_ci return err; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic int 76862306a36Sopenharmony_cimlxsw_sp_bridge_vlans_flood_set(struct mlxsw_sp_bridge_vlan *bridge_vlan, 76962306a36Sopenharmony_ci enum mlxsw_sp_flood_type packet_type, 77062306a36Sopenharmony_ci bool member) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 77362306a36Sopenharmony_ci int err; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci list_for_each_entry(mlxsw_sp_port_vlan, &bridge_vlan->port_vlan_list, 77662306a36Sopenharmony_ci bridge_vlan_node) { 77762306a36Sopenharmony_ci u16 local_port = mlxsw_sp_port_vlan->mlxsw_sp_port->local_port; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci err = mlxsw_sp_fid_flood_set(mlxsw_sp_port_vlan->fid, 78062306a36Sopenharmony_ci packet_type, local_port, member); 78162306a36Sopenharmony_ci if (err) 78262306a36Sopenharmony_ci goto err_fid_flood_set; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci return 0; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cierr_fid_flood_set: 78862306a36Sopenharmony_ci list_for_each_entry_continue_reverse(mlxsw_sp_port_vlan, 78962306a36Sopenharmony_ci &bridge_vlan->port_vlan_list, 79062306a36Sopenharmony_ci list) { 79162306a36Sopenharmony_ci u16 local_port = mlxsw_sp_port_vlan->mlxsw_sp_port->local_port; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci mlxsw_sp_fid_flood_set(mlxsw_sp_port_vlan->fid, packet_type, 79462306a36Sopenharmony_ci local_port, !member); 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci return err; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic int 80162306a36Sopenharmony_cimlxsw_sp_bridge_ports_flood_table_set(struct mlxsw_sp_bridge_port *bridge_port, 80262306a36Sopenharmony_ci enum mlxsw_sp_flood_type packet_type, 80362306a36Sopenharmony_ci bool member) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 80662306a36Sopenharmony_ci int err; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) { 80962306a36Sopenharmony_ci err = mlxsw_sp_bridge_vlans_flood_set(bridge_vlan, packet_type, 81062306a36Sopenharmony_ci member); 81162306a36Sopenharmony_ci if (err) 81262306a36Sopenharmony_ci goto err_bridge_vlans_flood_set; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci return 0; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cierr_bridge_vlans_flood_set: 81862306a36Sopenharmony_ci list_for_each_entry_continue_reverse(bridge_vlan, 81962306a36Sopenharmony_ci &bridge_port->vlans_list, list) 82062306a36Sopenharmony_ci mlxsw_sp_bridge_vlans_flood_set(bridge_vlan, packet_type, 82162306a36Sopenharmony_ci !member); 82262306a36Sopenharmony_ci return err; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic int 82662306a36Sopenharmony_cimlxsw_sp_port_bridge_vlan_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, 82762306a36Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan, 82862306a36Sopenharmony_ci bool set) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 83162306a36Sopenharmony_ci u16 vid = bridge_vlan->vid; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci list_for_each_entry(mlxsw_sp_port_vlan, &bridge_vlan->port_vlan_list, 83462306a36Sopenharmony_ci bridge_vlan_node) { 83562306a36Sopenharmony_ci if (mlxsw_sp_port_vlan->mlxsw_sp_port != mlxsw_sp_port) 83662306a36Sopenharmony_ci continue; 83762306a36Sopenharmony_ci return mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, set); 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return 0; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic int 84462306a36Sopenharmony_cimlxsw_sp_bridge_port_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, 84562306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 84662306a36Sopenharmony_ci bool set) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 84962306a36Sopenharmony_ci int err; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) { 85262306a36Sopenharmony_ci err = mlxsw_sp_port_bridge_vlan_learning_set(mlxsw_sp_port, 85362306a36Sopenharmony_ci bridge_vlan, set); 85462306a36Sopenharmony_ci if (err) 85562306a36Sopenharmony_ci goto err_port_bridge_vlan_learning_set; 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci return 0; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cierr_port_bridge_vlan_learning_set: 86162306a36Sopenharmony_ci list_for_each_entry_continue_reverse(bridge_vlan, 86262306a36Sopenharmony_ci &bridge_port->vlans_list, list) 86362306a36Sopenharmony_ci mlxsw_sp_port_bridge_vlan_learning_set(mlxsw_sp_port, 86462306a36Sopenharmony_ci bridge_vlan, !set); 86562306a36Sopenharmony_ci return err; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic int 86962306a36Sopenharmony_cimlxsw_sp_port_attr_br_pre_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, 87062306a36Sopenharmony_ci const struct net_device *orig_dev, 87162306a36Sopenharmony_ci struct switchdev_brport_flags flags, 87262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | 87562306a36Sopenharmony_ci BR_PORT_LOCKED | BR_PORT_MAB)) { 87662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported bridge port flag"); 87762306a36Sopenharmony_ci return -EINVAL; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if ((flags.mask & BR_PORT_LOCKED) && is_vlan_dev(orig_dev)) { 88162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Locked flag cannot be set on a VLAN upper"); 88262306a36Sopenharmony_ci return -EINVAL; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if ((flags.mask & BR_PORT_LOCKED) && vlan_uses_dev(orig_dev)) { 88662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Locked flag cannot be set on a bridge port that has VLAN uppers"); 88762306a36Sopenharmony_ci return -EINVAL; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci return 0; 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, 89462306a36Sopenharmony_ci struct net_device *orig_dev, 89562306a36Sopenharmony_ci struct switchdev_brport_flags flags) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 89862306a36Sopenharmony_ci int err; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge, 90162306a36Sopenharmony_ci orig_dev); 90262306a36Sopenharmony_ci if (!bridge_port) 90362306a36Sopenharmony_ci return 0; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (flags.mask & BR_FLOOD) { 90662306a36Sopenharmony_ci err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, 90762306a36Sopenharmony_ci bridge_port, 90862306a36Sopenharmony_ci MLXSW_SP_FLOOD_TYPE_UC, 90962306a36Sopenharmony_ci flags.val & BR_FLOOD); 91062306a36Sopenharmony_ci if (err) 91162306a36Sopenharmony_ci return err; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (flags.mask & BR_LEARNING) { 91562306a36Sopenharmony_ci err = mlxsw_sp_bridge_port_learning_set(mlxsw_sp_port, 91662306a36Sopenharmony_ci bridge_port, 91762306a36Sopenharmony_ci flags.val & BR_LEARNING); 91862306a36Sopenharmony_ci if (err) 91962306a36Sopenharmony_ci return err; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci if (flags.mask & BR_PORT_LOCKED) { 92362306a36Sopenharmony_ci err = mlxsw_sp_port_security_set(mlxsw_sp_port, 92462306a36Sopenharmony_ci flags.val & BR_PORT_LOCKED); 92562306a36Sopenharmony_ci if (err) 92662306a36Sopenharmony_ci return err; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if (bridge_port->bridge_device->multicast_enabled) 93062306a36Sopenharmony_ci goto out; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (flags.mask & BR_MCAST_FLOOD) { 93362306a36Sopenharmony_ci err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, 93462306a36Sopenharmony_ci bridge_port, 93562306a36Sopenharmony_ci MLXSW_SP_FLOOD_TYPE_MC, 93662306a36Sopenharmony_ci flags.val & BR_MCAST_FLOOD); 93762306a36Sopenharmony_ci if (err) 93862306a36Sopenharmony_ci return err; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ciout: 94262306a36Sopenharmony_ci memcpy(&bridge_port->flags, &flags.val, sizeof(flags.val)); 94362306a36Sopenharmony_ci return 0; 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic int mlxsw_sp_ageing_set(struct mlxsw_sp *mlxsw_sp, u32 ageing_time) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci char sfdat_pl[MLXSW_REG_SFDAT_LEN]; 94962306a36Sopenharmony_ci int err; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci mlxsw_reg_sfdat_pack(sfdat_pl, ageing_time); 95262306a36Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdat), sfdat_pl); 95362306a36Sopenharmony_ci if (err) 95462306a36Sopenharmony_ci return err; 95562306a36Sopenharmony_ci mlxsw_sp->bridge->ageing_time = ageing_time; 95662306a36Sopenharmony_ci return 0; 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port, 96062306a36Sopenharmony_ci unsigned long ageing_clock_t) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 96362306a36Sopenharmony_ci unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t); 96462306a36Sopenharmony_ci u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (ageing_time < MLXSW_SP_MIN_AGEING_TIME || 96762306a36Sopenharmony_ci ageing_time > MLXSW_SP_MAX_AGEING_TIME) 96862306a36Sopenharmony_ci return -ERANGE; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time); 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_cistatic int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, 97462306a36Sopenharmony_ci struct net_device *orig_dev, 97562306a36Sopenharmony_ci bool vlan_enabled) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 97862306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev); 98162306a36Sopenharmony_ci if (WARN_ON(!bridge_device)) 98262306a36Sopenharmony_ci return -EINVAL; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (bridge_device->vlan_enabled == vlan_enabled) 98562306a36Sopenharmony_ci return 0; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci netdev_err(bridge_device->dev, "VLAN filtering can't be changed for existing bridge\n"); 98862306a36Sopenharmony_ci return -EINVAL; 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_cistatic int mlxsw_sp_port_attr_br_vlan_proto_set(struct mlxsw_sp_port *mlxsw_sp_port, 99262306a36Sopenharmony_ci struct net_device *orig_dev, 99362306a36Sopenharmony_ci u16 vlan_proto) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 99662306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev); 99962306a36Sopenharmony_ci if (WARN_ON(!bridge_device)) 100062306a36Sopenharmony_ci return -EINVAL; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci netdev_err(bridge_device->dev, "VLAN protocol can't be changed on existing bridge\n"); 100362306a36Sopenharmony_ci return -EINVAL; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic int mlxsw_sp_port_attr_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port, 100762306a36Sopenharmony_ci struct net_device *orig_dev, 100862306a36Sopenharmony_ci bool is_port_mrouter) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 101162306a36Sopenharmony_ci int err; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge, 101462306a36Sopenharmony_ci orig_dev); 101562306a36Sopenharmony_ci if (!bridge_port) 101662306a36Sopenharmony_ci return 0; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci mlxsw_sp_port_mrouter_update_mdb(mlxsw_sp_port, bridge_port, 101962306a36Sopenharmony_ci is_port_mrouter); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (!bridge_port->bridge_device->multicast_enabled) 102262306a36Sopenharmony_ci goto out; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port, 102562306a36Sopenharmony_ci MLXSW_SP_FLOOD_TYPE_MC, 102662306a36Sopenharmony_ci is_port_mrouter); 102762306a36Sopenharmony_ci if (err) 102862306a36Sopenharmony_ci return err; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ciout: 103162306a36Sopenharmony_ci bridge_port->mrouter = is_port_mrouter; 103262306a36Sopenharmony_ci return 0; 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_cistatic bool mlxsw_sp_mc_flood(const struct mlxsw_sp_bridge_port *bridge_port) 103662306a36Sopenharmony_ci{ 103762306a36Sopenharmony_ci const struct mlxsw_sp_bridge_device *bridge_device; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci bridge_device = bridge_port->bridge_device; 104062306a36Sopenharmony_ci return bridge_device->multicast_enabled ? bridge_port->mrouter : 104162306a36Sopenharmony_ci bridge_port->flags & BR_MCAST_FLOOD; 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cistatic int mlxsw_sp_port_mc_disabled_set(struct mlxsw_sp_port *mlxsw_sp_port, 104562306a36Sopenharmony_ci struct net_device *orig_dev, 104662306a36Sopenharmony_ci bool mc_disabled) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci enum mlxsw_sp_flood_type packet_type = MLXSW_SP_FLOOD_TYPE_MC; 104962306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 105062306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 105162306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 105262306a36Sopenharmony_ci int err; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci /* It's possible we failed to enslave the port, yet this 105562306a36Sopenharmony_ci * operation is executed due to it being deferred. 105662306a36Sopenharmony_ci */ 105762306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev); 105862306a36Sopenharmony_ci if (!bridge_device) 105962306a36Sopenharmony_ci return 0; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci if (bridge_device->multicast_enabled == !mc_disabled) 106262306a36Sopenharmony_ci return 0; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci bridge_device->multicast_enabled = !mc_disabled; 106562306a36Sopenharmony_ci err = mlxsw_sp_bridge_mdb_mc_enable_sync(mlxsw_sp, bridge_device, 106662306a36Sopenharmony_ci !mc_disabled); 106762306a36Sopenharmony_ci if (err) 106862306a36Sopenharmony_ci goto err_mc_enable_sync; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci list_for_each_entry(bridge_port, &bridge_device->ports_list, list) { 107162306a36Sopenharmony_ci bool member = mlxsw_sp_mc_flood(bridge_port); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci err = mlxsw_sp_bridge_ports_flood_table_set(bridge_port, 107462306a36Sopenharmony_ci packet_type, 107562306a36Sopenharmony_ci member); 107662306a36Sopenharmony_ci if (err) 107762306a36Sopenharmony_ci goto err_flood_table_set; 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci return 0; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_cierr_flood_table_set: 108362306a36Sopenharmony_ci list_for_each_entry_continue_reverse(bridge_port, 108462306a36Sopenharmony_ci &bridge_device->ports_list, list) { 108562306a36Sopenharmony_ci bool member = mlxsw_sp_mc_flood(bridge_port); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci mlxsw_sp_bridge_ports_flood_table_set(bridge_port, packet_type, 108862306a36Sopenharmony_ci !member); 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci mlxsw_sp_bridge_mdb_mc_enable_sync(mlxsw_sp, bridge_device, 109162306a36Sopenharmony_ci mc_disabled); 109262306a36Sopenharmony_cierr_mc_enable_sync: 109362306a36Sopenharmony_ci bridge_device->multicast_enabled = mc_disabled; 109462306a36Sopenharmony_ci return err; 109562306a36Sopenharmony_ci} 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic struct mlxsw_sp_mdb_entry_port * 109862306a36Sopenharmony_cimlxsw_sp_mdb_entry_port_lookup(struct mlxsw_sp_mdb_entry *mdb_entry, 109962306a36Sopenharmony_ci u16 local_port) 110062306a36Sopenharmony_ci{ 110162306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry_port *mdb_entry_port; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci list_for_each_entry(mdb_entry_port, &mdb_entry->ports_list, list) { 110462306a36Sopenharmony_ci if (mdb_entry_port->local_port == local_port) 110562306a36Sopenharmony_ci return mdb_entry_port; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci return NULL; 110962306a36Sopenharmony_ci} 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_cistatic struct mlxsw_sp_mdb_entry_port * 111262306a36Sopenharmony_cimlxsw_sp_mdb_entry_port_get(struct mlxsw_sp *mlxsw_sp, 111362306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry, 111462306a36Sopenharmony_ci u16 local_port) 111562306a36Sopenharmony_ci{ 111662306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry_port *mdb_entry_port; 111762306a36Sopenharmony_ci int err; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port); 112062306a36Sopenharmony_ci if (mdb_entry_port) { 112162306a36Sopenharmony_ci if (mdb_entry_port->mrouter && 112262306a36Sopenharmony_ci refcount_read(&mdb_entry_port->refcount) == 1) 112362306a36Sopenharmony_ci mdb_entry->ports_count++; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci refcount_inc(&mdb_entry_port->refcount); 112662306a36Sopenharmony_ci return mdb_entry_port; 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci err = mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid, 113062306a36Sopenharmony_ci mdb_entry->key.fid, local_port, true); 113162306a36Sopenharmony_ci if (err) 113262306a36Sopenharmony_ci return ERR_PTR(err); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci mdb_entry_port = kzalloc(sizeof(*mdb_entry_port), GFP_KERNEL); 113562306a36Sopenharmony_ci if (!mdb_entry_port) { 113662306a36Sopenharmony_ci err = -ENOMEM; 113762306a36Sopenharmony_ci goto err_mdb_entry_port_alloc; 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci mdb_entry_port->local_port = local_port; 114162306a36Sopenharmony_ci refcount_set(&mdb_entry_port->refcount, 1); 114262306a36Sopenharmony_ci list_add(&mdb_entry_port->list, &mdb_entry->ports_list); 114362306a36Sopenharmony_ci mdb_entry->ports_count++; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci return mdb_entry_port; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_cierr_mdb_entry_port_alloc: 114862306a36Sopenharmony_ci mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid, 114962306a36Sopenharmony_ci mdb_entry->key.fid, local_port, false); 115062306a36Sopenharmony_ci return ERR_PTR(err); 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic void 115462306a36Sopenharmony_cimlxsw_sp_mdb_entry_port_put(struct mlxsw_sp *mlxsw_sp, 115562306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry, 115662306a36Sopenharmony_ci u16 local_port, bool force) 115762306a36Sopenharmony_ci{ 115862306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry_port *mdb_entry_port; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port); 116162306a36Sopenharmony_ci if (!mdb_entry_port) 116262306a36Sopenharmony_ci return; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci if (!force && !refcount_dec_and_test(&mdb_entry_port->refcount)) { 116562306a36Sopenharmony_ci if (mdb_entry_port->mrouter && 116662306a36Sopenharmony_ci refcount_read(&mdb_entry_port->refcount) == 1) 116762306a36Sopenharmony_ci mdb_entry->ports_count--; 116862306a36Sopenharmony_ci return; 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci mdb_entry->ports_count--; 117262306a36Sopenharmony_ci list_del(&mdb_entry_port->list); 117362306a36Sopenharmony_ci kfree(mdb_entry_port); 117462306a36Sopenharmony_ci mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid, 117562306a36Sopenharmony_ci mdb_entry->key.fid, local_port, false); 117662306a36Sopenharmony_ci} 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_cistatic __always_unused struct mlxsw_sp_mdb_entry_port * 117962306a36Sopenharmony_cimlxsw_sp_mdb_entry_mrouter_port_get(struct mlxsw_sp *mlxsw_sp, 118062306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry, 118162306a36Sopenharmony_ci u16 local_port) 118262306a36Sopenharmony_ci{ 118362306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry_port *mdb_entry_port; 118462306a36Sopenharmony_ci int err; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port); 118762306a36Sopenharmony_ci if (mdb_entry_port) { 118862306a36Sopenharmony_ci if (!mdb_entry_port->mrouter) 118962306a36Sopenharmony_ci refcount_inc(&mdb_entry_port->refcount); 119062306a36Sopenharmony_ci return mdb_entry_port; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci err = mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid, 119462306a36Sopenharmony_ci mdb_entry->key.fid, local_port, true); 119562306a36Sopenharmony_ci if (err) 119662306a36Sopenharmony_ci return ERR_PTR(err); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci mdb_entry_port = kzalloc(sizeof(*mdb_entry_port), GFP_KERNEL); 119962306a36Sopenharmony_ci if (!mdb_entry_port) { 120062306a36Sopenharmony_ci err = -ENOMEM; 120162306a36Sopenharmony_ci goto err_mdb_entry_port_alloc; 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci mdb_entry_port->local_port = local_port; 120562306a36Sopenharmony_ci refcount_set(&mdb_entry_port->refcount, 1); 120662306a36Sopenharmony_ci mdb_entry_port->mrouter = true; 120762306a36Sopenharmony_ci list_add(&mdb_entry_port->list, &mdb_entry->ports_list); 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci return mdb_entry_port; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cierr_mdb_entry_port_alloc: 121262306a36Sopenharmony_ci mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid, 121362306a36Sopenharmony_ci mdb_entry->key.fid, local_port, false); 121462306a36Sopenharmony_ci return ERR_PTR(err); 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_cistatic __always_unused void 121862306a36Sopenharmony_cimlxsw_sp_mdb_entry_mrouter_port_put(struct mlxsw_sp *mlxsw_sp, 121962306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry, 122062306a36Sopenharmony_ci u16 local_port) 122162306a36Sopenharmony_ci{ 122262306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry_port *mdb_entry_port; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port); 122562306a36Sopenharmony_ci if (!mdb_entry_port) 122662306a36Sopenharmony_ci return; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci if (!mdb_entry_port->mrouter) 122962306a36Sopenharmony_ci return; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci mdb_entry_port->mrouter = false; 123262306a36Sopenharmony_ci if (!refcount_dec_and_test(&mdb_entry_port->refcount)) 123362306a36Sopenharmony_ci return; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci list_del(&mdb_entry_port->list); 123662306a36Sopenharmony_ci kfree(mdb_entry_port); 123762306a36Sopenharmony_ci mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid, 123862306a36Sopenharmony_ci mdb_entry->key.fid, local_port, false); 123962306a36Sopenharmony_ci} 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_cistatic void 124262306a36Sopenharmony_cimlxsw_sp_bridge_mrouter_update_mdb(struct mlxsw_sp *mlxsw_sp, 124362306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 124462306a36Sopenharmony_ci bool add) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci u16 local_port = mlxsw_sp_router_port(mlxsw_sp); 124762306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) { 125062306a36Sopenharmony_ci if (add) 125162306a36Sopenharmony_ci mlxsw_sp_mdb_entry_mrouter_port_get(mlxsw_sp, mdb_entry, 125262306a36Sopenharmony_ci local_port); 125362306a36Sopenharmony_ci else 125462306a36Sopenharmony_ci mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, mdb_entry, 125562306a36Sopenharmony_ci local_port); 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci} 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_cistatic int 126062306a36Sopenharmony_cimlxsw_sp_port_attr_br_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port, 126162306a36Sopenharmony_ci struct net_device *orig_dev, 126262306a36Sopenharmony_ci bool is_mrouter) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 126562306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci /* It's possible we failed to enslave the port, yet this 126862306a36Sopenharmony_ci * operation is executed due to it being deferred. 126962306a36Sopenharmony_ci */ 127062306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev); 127162306a36Sopenharmony_ci if (!bridge_device) 127262306a36Sopenharmony_ci return 0; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci if (bridge_device->mrouter != is_mrouter) 127562306a36Sopenharmony_ci mlxsw_sp_bridge_mrouter_update_mdb(mlxsw_sp, bridge_device, 127662306a36Sopenharmony_ci is_mrouter); 127762306a36Sopenharmony_ci bridge_device->mrouter = is_mrouter; 127862306a36Sopenharmony_ci return 0; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_cistatic int mlxsw_sp_port_attr_set(struct net_device *dev, const void *ctx, 128262306a36Sopenharmony_ci const struct switchdev_attr *attr, 128362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 128662306a36Sopenharmony_ci int err; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci switch (attr->id) { 128962306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_STP_STATE: 129062306a36Sopenharmony_ci err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port, 129162306a36Sopenharmony_ci attr->orig_dev, 129262306a36Sopenharmony_ci attr->u.stp_state); 129362306a36Sopenharmony_ci break; 129462306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: 129562306a36Sopenharmony_ci err = mlxsw_sp_port_attr_br_pre_flags_set(mlxsw_sp_port, 129662306a36Sopenharmony_ci attr->orig_dev, 129762306a36Sopenharmony_ci attr->u.brport_flags, 129862306a36Sopenharmony_ci extack); 129962306a36Sopenharmony_ci break; 130062306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: 130162306a36Sopenharmony_ci err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port, 130262306a36Sopenharmony_ci attr->orig_dev, 130362306a36Sopenharmony_ci attr->u.brport_flags); 130462306a36Sopenharmony_ci break; 130562306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: 130662306a36Sopenharmony_ci err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, 130762306a36Sopenharmony_ci attr->u.ageing_time); 130862306a36Sopenharmony_ci break; 130962306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: 131062306a36Sopenharmony_ci err = mlxsw_sp_port_attr_br_vlan_set(mlxsw_sp_port, 131162306a36Sopenharmony_ci attr->orig_dev, 131262306a36Sopenharmony_ci attr->u.vlan_filtering); 131362306a36Sopenharmony_ci break; 131462306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_PROTOCOL: 131562306a36Sopenharmony_ci err = mlxsw_sp_port_attr_br_vlan_proto_set(mlxsw_sp_port, 131662306a36Sopenharmony_ci attr->orig_dev, 131762306a36Sopenharmony_ci attr->u.vlan_protocol); 131862306a36Sopenharmony_ci break; 131962306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_MROUTER: 132062306a36Sopenharmony_ci err = mlxsw_sp_port_attr_mrouter_set(mlxsw_sp_port, 132162306a36Sopenharmony_ci attr->orig_dev, 132262306a36Sopenharmony_ci attr->u.mrouter); 132362306a36Sopenharmony_ci break; 132462306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED: 132562306a36Sopenharmony_ci err = mlxsw_sp_port_mc_disabled_set(mlxsw_sp_port, 132662306a36Sopenharmony_ci attr->orig_dev, 132762306a36Sopenharmony_ci attr->u.mc_disabled); 132862306a36Sopenharmony_ci break; 132962306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER: 133062306a36Sopenharmony_ci err = mlxsw_sp_port_attr_br_mrouter_set(mlxsw_sp_port, 133162306a36Sopenharmony_ci attr->orig_dev, 133262306a36Sopenharmony_ci attr->u.mrouter); 133362306a36Sopenharmony_ci break; 133462306a36Sopenharmony_ci default: 133562306a36Sopenharmony_ci err = -EOPNOTSUPP; 133662306a36Sopenharmony_ci break; 133762306a36Sopenharmony_ci } 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp); 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci return err; 134262306a36Sopenharmony_ci} 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_cistatic int 134562306a36Sopenharmony_cimlxsw_sp_port_vlan_fid_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan, 134662306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 134762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port; 135062306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 135162306a36Sopenharmony_ci u16 local_port = mlxsw_sp_port->local_port; 135262306a36Sopenharmony_ci u16 vid = mlxsw_sp_port_vlan->vid; 135362306a36Sopenharmony_ci struct mlxsw_sp_fid *fid; 135462306a36Sopenharmony_ci int err; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci bridge_device = bridge_port->bridge_device; 135762306a36Sopenharmony_ci fid = bridge_device->ops->fid_get(bridge_device, vid, extack); 135862306a36Sopenharmony_ci if (IS_ERR(fid)) 135962306a36Sopenharmony_ci return PTR_ERR(fid); 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci err = mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_UC, local_port, 136262306a36Sopenharmony_ci bridge_port->flags & BR_FLOOD); 136362306a36Sopenharmony_ci if (err) 136462306a36Sopenharmony_ci goto err_fid_uc_flood_set; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci err = mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_MC, local_port, 136762306a36Sopenharmony_ci mlxsw_sp_mc_flood(bridge_port)); 136862306a36Sopenharmony_ci if (err) 136962306a36Sopenharmony_ci goto err_fid_mc_flood_set; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci err = mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_BC, local_port, 137262306a36Sopenharmony_ci true); 137362306a36Sopenharmony_ci if (err) 137462306a36Sopenharmony_ci goto err_fid_bc_flood_set; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid); 137762306a36Sopenharmony_ci if (err) 137862306a36Sopenharmony_ci goto err_fid_port_vid_map; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci mlxsw_sp_port_vlan->fid = fid; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci return 0; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cierr_fid_port_vid_map: 138562306a36Sopenharmony_ci mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_BC, local_port, false); 138662306a36Sopenharmony_cierr_fid_bc_flood_set: 138762306a36Sopenharmony_ci mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_MC, local_port, false); 138862306a36Sopenharmony_cierr_fid_mc_flood_set: 138962306a36Sopenharmony_ci mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_UC, local_port, false); 139062306a36Sopenharmony_cierr_fid_uc_flood_set: 139162306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 139262306a36Sopenharmony_ci return err; 139362306a36Sopenharmony_ci} 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_cistatic void 139662306a36Sopenharmony_cimlxsw_sp_port_vlan_fid_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port; 139962306a36Sopenharmony_ci struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid; 140062306a36Sopenharmony_ci u16 local_port = mlxsw_sp_port->local_port; 140162306a36Sopenharmony_ci u16 vid = mlxsw_sp_port_vlan->vid; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci mlxsw_sp_port_vlan->fid = NULL; 140462306a36Sopenharmony_ci mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid); 140562306a36Sopenharmony_ci mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_BC, local_port, false); 140662306a36Sopenharmony_ci mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_MC, local_port, false); 140762306a36Sopenharmony_ci mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_UC, local_port, false); 140862306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 140962306a36Sopenharmony_ci} 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_cistatic u16 141262306a36Sopenharmony_cimlxsw_sp_port_pvid_determine(const struct mlxsw_sp_port *mlxsw_sp_port, 141362306a36Sopenharmony_ci u16 vid, bool is_pvid) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci if (is_pvid) 141662306a36Sopenharmony_ci return vid; 141762306a36Sopenharmony_ci else if (mlxsw_sp_port->pvid == vid) 141862306a36Sopenharmony_ci return 0; /* Dis-allow untagged packets */ 141962306a36Sopenharmony_ci else 142062306a36Sopenharmony_ci return mlxsw_sp_port->pvid; 142162306a36Sopenharmony_ci} 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_cistatic int 142462306a36Sopenharmony_cimlxsw_sp_port_vlan_bridge_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan, 142562306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 142662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 142762306a36Sopenharmony_ci{ 142862306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port; 142962306a36Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 143062306a36Sopenharmony_ci u16 vid = mlxsw_sp_port_vlan->vid; 143162306a36Sopenharmony_ci int err; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci /* No need to continue if only VLAN flags were changed */ 143462306a36Sopenharmony_ci if (mlxsw_sp_port_vlan->bridge_port) 143562306a36Sopenharmony_ci return 0; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci err = mlxsw_sp_port_vlan_fid_join(mlxsw_sp_port_vlan, bridge_port, 143862306a36Sopenharmony_ci extack); 143962306a36Sopenharmony_ci if (err) 144062306a36Sopenharmony_ci return err; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, 144362306a36Sopenharmony_ci bridge_port->flags & BR_LEARNING); 144462306a36Sopenharmony_ci if (err) 144562306a36Sopenharmony_ci goto err_port_vid_learning_set; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, 144862306a36Sopenharmony_ci bridge_port->stp_state); 144962306a36Sopenharmony_ci if (err) 145062306a36Sopenharmony_ci goto err_port_vid_stp_set; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci bridge_vlan = mlxsw_sp_bridge_vlan_get(bridge_port, vid); 145362306a36Sopenharmony_ci if (!bridge_vlan) { 145462306a36Sopenharmony_ci err = -ENOMEM; 145562306a36Sopenharmony_ci goto err_bridge_vlan_get; 145662306a36Sopenharmony_ci } 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci list_add(&mlxsw_sp_port_vlan->bridge_vlan_node, 145962306a36Sopenharmony_ci &bridge_vlan->port_vlan_list); 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci mlxsw_sp_bridge_port_get(mlxsw_sp_port->mlxsw_sp->bridge, 146262306a36Sopenharmony_ci bridge_port->dev, extack); 146362306a36Sopenharmony_ci mlxsw_sp_port_vlan->bridge_port = bridge_port; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci return 0; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_cierr_bridge_vlan_get: 146862306a36Sopenharmony_ci mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_DISABLED); 146962306a36Sopenharmony_cierr_port_vid_stp_set: 147062306a36Sopenharmony_ci mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false); 147162306a36Sopenharmony_cierr_port_vid_learning_set: 147262306a36Sopenharmony_ci mlxsw_sp_port_vlan_fid_leave(mlxsw_sp_port_vlan); 147362306a36Sopenharmony_ci return err; 147462306a36Sopenharmony_ci} 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_civoid 147762306a36Sopenharmony_cimlxsw_sp_port_vlan_bridge_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan) 147862306a36Sopenharmony_ci{ 147962306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port; 148062306a36Sopenharmony_ci struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid; 148162306a36Sopenharmony_ci struct mlxsw_sp_bridge_vlan *bridge_vlan; 148262306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 148362306a36Sopenharmony_ci u16 vid = mlxsw_sp_port_vlan->vid; 148462306a36Sopenharmony_ci bool last_port; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_8021Q && 148762306a36Sopenharmony_ci mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_8021D)) 148862306a36Sopenharmony_ci return; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci bridge_port = mlxsw_sp_port_vlan->bridge_port; 149162306a36Sopenharmony_ci bridge_vlan = mlxsw_sp_bridge_vlan_find(bridge_port, vid); 149262306a36Sopenharmony_ci last_port = list_is_singular(&bridge_vlan->port_vlan_list); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci list_del(&mlxsw_sp_port_vlan->bridge_vlan_node); 149562306a36Sopenharmony_ci mlxsw_sp_bridge_vlan_put(bridge_vlan); 149662306a36Sopenharmony_ci mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_DISABLED); 149762306a36Sopenharmony_ci mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false); 149862306a36Sopenharmony_ci if (last_port) 149962306a36Sopenharmony_ci mlxsw_sp_bridge_port_fdb_flush(mlxsw_sp_port->mlxsw_sp, 150062306a36Sopenharmony_ci bridge_port, 150162306a36Sopenharmony_ci mlxsw_sp_fid_index(fid)); 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci mlxsw_sp_bridge_port_mdb_flush(mlxsw_sp_port, bridge_port, 150462306a36Sopenharmony_ci mlxsw_sp_fid_index(fid)); 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci mlxsw_sp_port_vlan_fid_leave(mlxsw_sp_port_vlan); 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci mlxsw_sp_bridge_port_put(mlxsw_sp_port->mlxsw_sp->bridge, bridge_port); 150962306a36Sopenharmony_ci mlxsw_sp_port_vlan->bridge_port = NULL; 151062306a36Sopenharmony_ci} 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_cistatic int 151362306a36Sopenharmony_cimlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port, 151462306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 151562306a36Sopenharmony_ci u16 vid, bool is_untagged, bool is_pvid, 151662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 151762306a36Sopenharmony_ci{ 151862306a36Sopenharmony_ci u16 pvid = mlxsw_sp_port_pvid_determine(mlxsw_sp_port, vid, is_pvid); 151962306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 152062306a36Sopenharmony_ci u16 old_pvid = mlxsw_sp_port->pvid; 152162306a36Sopenharmony_ci u16 proto; 152262306a36Sopenharmony_ci int err; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci /* The only valid scenario in which a port-vlan already exists, is if 152562306a36Sopenharmony_ci * the VLAN flags were changed and the port-vlan is associated with the 152662306a36Sopenharmony_ci * correct bridge port 152762306a36Sopenharmony_ci */ 152862306a36Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid); 152962306a36Sopenharmony_ci if (mlxsw_sp_port_vlan && 153062306a36Sopenharmony_ci mlxsw_sp_port_vlan->bridge_port != bridge_port) 153162306a36Sopenharmony_ci return -EEXIST; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci if (!mlxsw_sp_port_vlan) { 153462306a36Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_create(mlxsw_sp_port, 153562306a36Sopenharmony_ci vid); 153662306a36Sopenharmony_ci if (IS_ERR(mlxsw_sp_port_vlan)) 153762306a36Sopenharmony_ci return PTR_ERR(mlxsw_sp_port_vlan); 153862306a36Sopenharmony_ci } 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, 154162306a36Sopenharmony_ci is_untagged); 154262306a36Sopenharmony_ci if (err) 154362306a36Sopenharmony_ci goto err_port_vlan_set; 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci br_vlan_get_proto(bridge_port->bridge_device->dev, &proto); 154662306a36Sopenharmony_ci err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, pvid, proto); 154762306a36Sopenharmony_ci if (err) 154862306a36Sopenharmony_ci goto err_port_pvid_set; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci err = mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port, 155162306a36Sopenharmony_ci extack); 155262306a36Sopenharmony_ci if (err) 155362306a36Sopenharmony_ci goto err_port_vlan_bridge_join; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci return 0; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_cierr_port_vlan_bridge_join: 155862306a36Sopenharmony_ci mlxsw_sp_port_pvid_set(mlxsw_sp_port, old_pvid, proto); 155962306a36Sopenharmony_cierr_port_pvid_set: 156062306a36Sopenharmony_ci mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); 156162306a36Sopenharmony_cierr_port_vlan_set: 156262306a36Sopenharmony_ci mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan); 156362306a36Sopenharmony_ci return err; 156462306a36Sopenharmony_ci} 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_cistatic int 156762306a36Sopenharmony_cimlxsw_sp_br_rif_pvid_change(struct mlxsw_sp *mlxsw_sp, 156862306a36Sopenharmony_ci struct net_device *br_dev, 156962306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan, 157062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 157162306a36Sopenharmony_ci{ 157262306a36Sopenharmony_ci bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci return mlxsw_sp_router_bridge_vlan_add(mlxsw_sp, br_dev, vlan->vid, 157562306a36Sopenharmony_ci flag_pvid, extack); 157662306a36Sopenharmony_ci} 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_cistatic int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, 157962306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan, 158062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 158162306a36Sopenharmony_ci{ 158262306a36Sopenharmony_ci bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 158362306a36Sopenharmony_ci bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; 158462306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 158562306a36Sopenharmony_ci struct net_device *orig_dev = vlan->obj.orig_dev; 158662306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci if (netif_is_bridge_master(orig_dev)) { 158962306a36Sopenharmony_ci int err = 0; 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci if (br_vlan_enabled(orig_dev)) 159262306a36Sopenharmony_ci err = mlxsw_sp_br_rif_pvid_change(mlxsw_sp, orig_dev, 159362306a36Sopenharmony_ci vlan, extack); 159462306a36Sopenharmony_ci if (!err) 159562306a36Sopenharmony_ci err = -EOPNOTSUPP; 159662306a36Sopenharmony_ci return err; 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); 160062306a36Sopenharmony_ci if (WARN_ON(!bridge_port)) 160162306a36Sopenharmony_ci return -EINVAL; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci if (!bridge_port->bridge_device->vlan_enabled) 160462306a36Sopenharmony_ci return 0; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci return mlxsw_sp_bridge_port_vlan_add(mlxsw_sp_port, bridge_port, 160762306a36Sopenharmony_ci vlan->vid, flag_untagged, 160862306a36Sopenharmony_ci flag_pvid, extack); 160962306a36Sopenharmony_ci} 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_cistatic enum mlxsw_reg_sfdf_flush_type mlxsw_sp_fdb_flush_type(bool lagged) 161262306a36Sopenharmony_ci{ 161362306a36Sopenharmony_ci return lagged ? MLXSW_REG_SFDF_FLUSH_PER_LAG_AND_FID : 161462306a36Sopenharmony_ci MLXSW_REG_SFDF_FLUSH_PER_PORT_AND_FID; 161562306a36Sopenharmony_ci} 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_cistatic int 161862306a36Sopenharmony_cimlxsw_sp_bridge_port_fdb_flush(struct mlxsw_sp *mlxsw_sp, 161962306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 162062306a36Sopenharmony_ci u16 fid_index) 162162306a36Sopenharmony_ci{ 162262306a36Sopenharmony_ci bool lagged = bridge_port->lagged; 162362306a36Sopenharmony_ci char sfdf_pl[MLXSW_REG_SFDF_LEN]; 162462306a36Sopenharmony_ci u16 system_port; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci system_port = lagged ? bridge_port->lag_id : bridge_port->system_port; 162762306a36Sopenharmony_ci mlxsw_reg_sfdf_pack(sfdf_pl, mlxsw_sp_fdb_flush_type(lagged)); 162862306a36Sopenharmony_ci mlxsw_reg_sfdf_fid_set(sfdf_pl, fid_index); 162962306a36Sopenharmony_ci mlxsw_reg_sfdf_port_fid_system_port_set(sfdf_pl, system_port); 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdf), sfdf_pl); 163262306a36Sopenharmony_ci} 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_cistatic enum mlxsw_reg_sfd_rec_policy mlxsw_sp_sfd_rec_policy(bool dynamic) 163562306a36Sopenharmony_ci{ 163662306a36Sopenharmony_ci return dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS : 163762306a36Sopenharmony_ci MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_MLAG; 163862306a36Sopenharmony_ci} 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_cistatic enum mlxsw_reg_sfd_op mlxsw_sp_sfd_op(bool adding) 164162306a36Sopenharmony_ci{ 164262306a36Sopenharmony_ci return adding ? MLXSW_REG_SFD_OP_WRITE_EDIT : 164362306a36Sopenharmony_ci MLXSW_REG_SFD_OP_WRITE_REMOVE; 164462306a36Sopenharmony_ci} 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_cistatic int 164762306a36Sopenharmony_cimlxsw_sp_port_fdb_tun_uc_op4(struct mlxsw_sp *mlxsw_sp, bool dynamic, 164862306a36Sopenharmony_ci const char *mac, u16 fid, __be32 addr, bool adding) 164962306a36Sopenharmony_ci{ 165062306a36Sopenharmony_ci char *sfd_pl; 165162306a36Sopenharmony_ci u8 num_rec; 165262306a36Sopenharmony_ci u32 uip; 165362306a36Sopenharmony_ci int err; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); 165662306a36Sopenharmony_ci if (!sfd_pl) 165762306a36Sopenharmony_ci return -ENOMEM; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci uip = be32_to_cpu(addr); 166062306a36Sopenharmony_ci mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); 166162306a36Sopenharmony_ci mlxsw_reg_sfd_uc_tunnel_pack4(sfd_pl, 0, 166262306a36Sopenharmony_ci mlxsw_sp_sfd_rec_policy(dynamic), mac, 166362306a36Sopenharmony_ci fid, MLXSW_REG_SFD_REC_ACTION_NOP, uip); 166462306a36Sopenharmony_ci num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); 166562306a36Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); 166662306a36Sopenharmony_ci if (err) 166762306a36Sopenharmony_ci goto out; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl)) 167062306a36Sopenharmony_ci err = -EBUSY; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ciout: 167362306a36Sopenharmony_ci kfree(sfd_pl); 167462306a36Sopenharmony_ci return err; 167562306a36Sopenharmony_ci} 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_cistatic int mlxsw_sp_port_fdb_tun_uc_op6_sfd_write(struct mlxsw_sp *mlxsw_sp, 167862306a36Sopenharmony_ci const char *mac, u16 fid, 167962306a36Sopenharmony_ci u32 kvdl_index, bool adding) 168062306a36Sopenharmony_ci{ 168162306a36Sopenharmony_ci char *sfd_pl; 168262306a36Sopenharmony_ci u8 num_rec; 168362306a36Sopenharmony_ci int err; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); 168662306a36Sopenharmony_ci if (!sfd_pl) 168762306a36Sopenharmony_ci return -ENOMEM; 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); 169062306a36Sopenharmony_ci mlxsw_reg_sfd_uc_tunnel_pack6(sfd_pl, 0, mac, fid, 169162306a36Sopenharmony_ci MLXSW_REG_SFD_REC_ACTION_NOP, kvdl_index); 169262306a36Sopenharmony_ci num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); 169362306a36Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); 169462306a36Sopenharmony_ci if (err) 169562306a36Sopenharmony_ci goto out; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl)) 169862306a36Sopenharmony_ci err = -EBUSY; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ciout: 170162306a36Sopenharmony_ci kfree(sfd_pl); 170262306a36Sopenharmony_ci return err; 170362306a36Sopenharmony_ci} 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_cistatic int mlxsw_sp_port_fdb_tun_uc_op6_add(struct mlxsw_sp *mlxsw_sp, 170662306a36Sopenharmony_ci const char *mac, u16 fid, 170762306a36Sopenharmony_ci const struct in6_addr *addr) 170862306a36Sopenharmony_ci{ 170962306a36Sopenharmony_ci u32 kvdl_index; 171062306a36Sopenharmony_ci int err; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci err = mlxsw_sp_nve_ipv6_addr_kvdl_set(mlxsw_sp, addr, &kvdl_index); 171362306a36Sopenharmony_ci if (err) 171462306a36Sopenharmony_ci return err; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci err = mlxsw_sp_port_fdb_tun_uc_op6_sfd_write(mlxsw_sp, mac, fid, 171762306a36Sopenharmony_ci kvdl_index, true); 171862306a36Sopenharmony_ci if (err) 171962306a36Sopenharmony_ci goto err_sfd_write; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci err = mlxsw_sp_nve_ipv6_addr_map_replace(mlxsw_sp, mac, fid, addr); 172262306a36Sopenharmony_ci if (err) 172362306a36Sopenharmony_ci /* Replace can fail only for creating new mapping, so removing 172462306a36Sopenharmony_ci * the FDB entry in the error path is OK. 172562306a36Sopenharmony_ci */ 172662306a36Sopenharmony_ci goto err_addr_replace; 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci return 0; 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_cierr_addr_replace: 173162306a36Sopenharmony_ci mlxsw_sp_port_fdb_tun_uc_op6_sfd_write(mlxsw_sp, mac, fid, kvdl_index, 173262306a36Sopenharmony_ci false); 173362306a36Sopenharmony_cierr_sfd_write: 173462306a36Sopenharmony_ci mlxsw_sp_nve_ipv6_addr_kvdl_unset(mlxsw_sp, addr); 173562306a36Sopenharmony_ci return err; 173662306a36Sopenharmony_ci} 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_cistatic void mlxsw_sp_port_fdb_tun_uc_op6_del(struct mlxsw_sp *mlxsw_sp, 173962306a36Sopenharmony_ci const char *mac, u16 fid, 174062306a36Sopenharmony_ci const struct in6_addr *addr) 174162306a36Sopenharmony_ci{ 174262306a36Sopenharmony_ci mlxsw_sp_nve_ipv6_addr_map_del(mlxsw_sp, mac, fid); 174362306a36Sopenharmony_ci mlxsw_sp_port_fdb_tun_uc_op6_sfd_write(mlxsw_sp, mac, fid, 0, false); 174462306a36Sopenharmony_ci mlxsw_sp_nve_ipv6_addr_kvdl_unset(mlxsw_sp, addr); 174562306a36Sopenharmony_ci} 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_cistatic int 174862306a36Sopenharmony_cimlxsw_sp_port_fdb_tun_uc_op6(struct mlxsw_sp *mlxsw_sp, const char *mac, 174962306a36Sopenharmony_ci u16 fid, const struct in6_addr *addr, bool adding) 175062306a36Sopenharmony_ci{ 175162306a36Sopenharmony_ci if (adding) 175262306a36Sopenharmony_ci return mlxsw_sp_port_fdb_tun_uc_op6_add(mlxsw_sp, mac, fid, 175362306a36Sopenharmony_ci addr); 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci mlxsw_sp_port_fdb_tun_uc_op6_del(mlxsw_sp, mac, fid, addr); 175662306a36Sopenharmony_ci return 0; 175762306a36Sopenharmony_ci} 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_cistatic int mlxsw_sp_port_fdb_tunnel_uc_op(struct mlxsw_sp *mlxsw_sp, 176062306a36Sopenharmony_ci const char *mac, u16 fid, 176162306a36Sopenharmony_ci enum mlxsw_sp_l3proto proto, 176262306a36Sopenharmony_ci const union mlxsw_sp_l3addr *addr, 176362306a36Sopenharmony_ci bool adding, bool dynamic) 176462306a36Sopenharmony_ci{ 176562306a36Sopenharmony_ci switch (proto) { 176662306a36Sopenharmony_ci case MLXSW_SP_L3_PROTO_IPV4: 176762306a36Sopenharmony_ci return mlxsw_sp_port_fdb_tun_uc_op4(mlxsw_sp, dynamic, mac, fid, 176862306a36Sopenharmony_ci addr->addr4, adding); 176962306a36Sopenharmony_ci case MLXSW_SP_L3_PROTO_IPV6: 177062306a36Sopenharmony_ci return mlxsw_sp_port_fdb_tun_uc_op6(mlxsw_sp, mac, fid, 177162306a36Sopenharmony_ci &addr->addr6, adding); 177262306a36Sopenharmony_ci default: 177362306a36Sopenharmony_ci WARN_ON(1); 177462306a36Sopenharmony_ci return -EOPNOTSUPP; 177562306a36Sopenharmony_ci } 177662306a36Sopenharmony_ci} 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_cistatic int __mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u16 local_port, 177962306a36Sopenharmony_ci const char *mac, u16 fid, u16 vid, 178062306a36Sopenharmony_ci bool adding, 178162306a36Sopenharmony_ci enum mlxsw_reg_sfd_rec_action action, 178262306a36Sopenharmony_ci enum mlxsw_reg_sfd_rec_policy policy) 178362306a36Sopenharmony_ci{ 178462306a36Sopenharmony_ci char *sfd_pl; 178562306a36Sopenharmony_ci u8 num_rec; 178662306a36Sopenharmony_ci int err; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); 178962306a36Sopenharmony_ci if (!sfd_pl) 179062306a36Sopenharmony_ci return -ENOMEM; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); 179362306a36Sopenharmony_ci mlxsw_reg_sfd_uc_pack(sfd_pl, 0, policy, mac, fid, vid, action, 179462306a36Sopenharmony_ci local_port); 179562306a36Sopenharmony_ci num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); 179662306a36Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); 179762306a36Sopenharmony_ci if (err) 179862306a36Sopenharmony_ci goto out; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl)) 180162306a36Sopenharmony_ci err = -EBUSY; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ciout: 180462306a36Sopenharmony_ci kfree(sfd_pl); 180562306a36Sopenharmony_ci return err; 180662306a36Sopenharmony_ci} 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_cistatic int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u16 local_port, 180962306a36Sopenharmony_ci const char *mac, u16 fid, u16 vid, 181062306a36Sopenharmony_ci bool adding, bool dynamic) 181162306a36Sopenharmony_ci{ 181262306a36Sopenharmony_ci return __mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid, vid, 181362306a36Sopenharmony_ci adding, MLXSW_REG_SFD_REC_ACTION_NOP, 181462306a36Sopenharmony_ci mlxsw_sp_sfd_rec_policy(dynamic)); 181562306a36Sopenharmony_ci} 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ciint mlxsw_sp_rif_fdb_op(struct mlxsw_sp *mlxsw_sp, const char *mac, u16 fid, 181862306a36Sopenharmony_ci bool adding) 181962306a36Sopenharmony_ci{ 182062306a36Sopenharmony_ci return __mlxsw_sp_port_fdb_uc_op(mlxsw_sp, 0, mac, fid, 0, adding, 182162306a36Sopenharmony_ci MLXSW_REG_SFD_REC_ACTION_FORWARD_IP_ROUTER, 182262306a36Sopenharmony_ci MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY); 182362306a36Sopenharmony_ci} 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_cistatic int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id, 182662306a36Sopenharmony_ci const char *mac, u16 fid, u16 lag_vid, 182762306a36Sopenharmony_ci bool adding, bool dynamic) 182862306a36Sopenharmony_ci{ 182962306a36Sopenharmony_ci char *sfd_pl; 183062306a36Sopenharmony_ci u8 num_rec; 183162306a36Sopenharmony_ci int err; 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); 183462306a36Sopenharmony_ci if (!sfd_pl) 183562306a36Sopenharmony_ci return -ENOMEM; 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); 183862306a36Sopenharmony_ci mlxsw_reg_sfd_uc_lag_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic), 183962306a36Sopenharmony_ci mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP, 184062306a36Sopenharmony_ci lag_vid, lag_id); 184162306a36Sopenharmony_ci num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); 184262306a36Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); 184362306a36Sopenharmony_ci if (err) 184462306a36Sopenharmony_ci goto out; 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl)) 184762306a36Sopenharmony_ci err = -EBUSY; 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ciout: 185062306a36Sopenharmony_ci kfree(sfd_pl); 185162306a36Sopenharmony_ci return err; 185262306a36Sopenharmony_ci} 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_cistatic int 185562306a36Sopenharmony_cimlxsw_sp_port_fdb_set(struct mlxsw_sp_port *mlxsw_sp_port, 185662306a36Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info, bool adding) 185762306a36Sopenharmony_ci{ 185862306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 185962306a36Sopenharmony_ci struct net_device *orig_dev = fdb_info->info.dev; 186062306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 186162306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 186262306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 186362306a36Sopenharmony_ci u16 fid_index, vid; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); 186662306a36Sopenharmony_ci if (!bridge_port) 186762306a36Sopenharmony_ci return -EINVAL; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci bridge_device = bridge_port->bridge_device; 187062306a36Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port, 187162306a36Sopenharmony_ci bridge_device, 187262306a36Sopenharmony_ci fdb_info->vid); 187362306a36Sopenharmony_ci if (!mlxsw_sp_port_vlan) 187462306a36Sopenharmony_ci return 0; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); 187762306a36Sopenharmony_ci vid = mlxsw_sp_port_vlan->vid; 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci if (!bridge_port->lagged) 188062306a36Sopenharmony_ci return mlxsw_sp_port_fdb_uc_op(mlxsw_sp, 188162306a36Sopenharmony_ci bridge_port->system_port, 188262306a36Sopenharmony_ci fdb_info->addr, fid_index, vid, 188362306a36Sopenharmony_ci adding, false); 188462306a36Sopenharmony_ci else 188562306a36Sopenharmony_ci return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, 188662306a36Sopenharmony_ci bridge_port->lag_id, 188762306a36Sopenharmony_ci fdb_info->addr, fid_index, 188862306a36Sopenharmony_ci vid, adding, false); 188962306a36Sopenharmony_ci} 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_cistatic int mlxsw_sp_mdb_entry_write(struct mlxsw_sp *mlxsw_sp, 189262306a36Sopenharmony_ci const struct mlxsw_sp_mdb_entry *mdb_entry, 189362306a36Sopenharmony_ci bool adding) 189462306a36Sopenharmony_ci{ 189562306a36Sopenharmony_ci char *sfd_pl; 189662306a36Sopenharmony_ci u8 num_rec; 189762306a36Sopenharmony_ci int err; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); 190062306a36Sopenharmony_ci if (!sfd_pl) 190162306a36Sopenharmony_ci return -ENOMEM; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); 190462306a36Sopenharmony_ci mlxsw_reg_sfd_mc_pack(sfd_pl, 0, mdb_entry->key.addr, 190562306a36Sopenharmony_ci mdb_entry->key.fid, MLXSW_REG_SFD_REC_ACTION_NOP, 190662306a36Sopenharmony_ci mdb_entry->mid); 190762306a36Sopenharmony_ci num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); 190862306a36Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); 190962306a36Sopenharmony_ci if (err) 191062306a36Sopenharmony_ci goto out; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl)) 191362306a36Sopenharmony_ci err = -EBUSY; 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ciout: 191662306a36Sopenharmony_ci kfree(sfd_pl); 191762306a36Sopenharmony_ci return err; 191862306a36Sopenharmony_ci} 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_cistatic void 192162306a36Sopenharmony_cimlxsw_sp_bridge_port_get_ports_bitmap(struct mlxsw_sp *mlxsw_sp, 192262306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 192362306a36Sopenharmony_ci struct mlxsw_sp_ports_bitmap *ports_bm) 192462306a36Sopenharmony_ci{ 192562306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 192662306a36Sopenharmony_ci u64 max_lag_members, i; 192762306a36Sopenharmony_ci int lag_id; 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci if (!bridge_port->lagged) { 193062306a36Sopenharmony_ci set_bit(bridge_port->system_port, ports_bm->bitmap); 193162306a36Sopenharmony_ci } else { 193262306a36Sopenharmony_ci max_lag_members = MLXSW_CORE_RES_GET(mlxsw_sp->core, 193362306a36Sopenharmony_ci MAX_LAG_MEMBERS); 193462306a36Sopenharmony_ci lag_id = bridge_port->lag_id; 193562306a36Sopenharmony_ci for (i = 0; i < max_lag_members; i++) { 193662306a36Sopenharmony_ci mlxsw_sp_port = mlxsw_sp_port_lagged_get(mlxsw_sp, 193762306a36Sopenharmony_ci lag_id, i); 193862306a36Sopenharmony_ci if (mlxsw_sp_port) 193962306a36Sopenharmony_ci set_bit(mlxsw_sp_port->local_port, 194062306a36Sopenharmony_ci ports_bm->bitmap); 194162306a36Sopenharmony_ci } 194262306a36Sopenharmony_ci } 194362306a36Sopenharmony_ci} 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_cistatic void 194662306a36Sopenharmony_cimlxsw_sp_mc_get_mrouters_bitmap(struct mlxsw_sp_ports_bitmap *flood_bm, 194762306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 194862306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp) 194962306a36Sopenharmony_ci{ 195062306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci list_for_each_entry(bridge_port, &bridge_device->ports_list, list) { 195362306a36Sopenharmony_ci if (bridge_port->mrouter) { 195462306a36Sopenharmony_ci mlxsw_sp_bridge_port_get_ports_bitmap(mlxsw_sp, 195562306a36Sopenharmony_ci bridge_port, 195662306a36Sopenharmony_ci flood_bm); 195762306a36Sopenharmony_ci } 195862306a36Sopenharmony_ci } 195962306a36Sopenharmony_ci} 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_cistatic int mlxsw_sp_mc_mdb_mrouters_add(struct mlxsw_sp *mlxsw_sp, 196262306a36Sopenharmony_ci struct mlxsw_sp_ports_bitmap *ports_bm, 196362306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry) 196462306a36Sopenharmony_ci{ 196562306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry_port *mdb_entry_port; 196662306a36Sopenharmony_ci unsigned int nbits = ports_bm->nbits; 196762306a36Sopenharmony_ci int i; 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci for_each_set_bit(i, ports_bm->bitmap, nbits) { 197062306a36Sopenharmony_ci mdb_entry_port = mlxsw_sp_mdb_entry_mrouter_port_get(mlxsw_sp, 197162306a36Sopenharmony_ci mdb_entry, 197262306a36Sopenharmony_ci i); 197362306a36Sopenharmony_ci if (IS_ERR(mdb_entry_port)) { 197462306a36Sopenharmony_ci nbits = i; 197562306a36Sopenharmony_ci goto err_mrouter_port_get; 197662306a36Sopenharmony_ci } 197762306a36Sopenharmony_ci } 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci return 0; 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_cierr_mrouter_port_get: 198262306a36Sopenharmony_ci for_each_set_bit(i, ports_bm->bitmap, nbits) 198362306a36Sopenharmony_ci mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, mdb_entry, i); 198462306a36Sopenharmony_ci return PTR_ERR(mdb_entry_port); 198562306a36Sopenharmony_ci} 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_cistatic void mlxsw_sp_mc_mdb_mrouters_del(struct mlxsw_sp *mlxsw_sp, 198862306a36Sopenharmony_ci struct mlxsw_sp_ports_bitmap *ports_bm, 198962306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry) 199062306a36Sopenharmony_ci{ 199162306a36Sopenharmony_ci int i; 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci for_each_set_bit(i, ports_bm->bitmap, ports_bm->nbits) 199462306a36Sopenharmony_ci mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, mdb_entry, i); 199562306a36Sopenharmony_ci} 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_cistatic int 199862306a36Sopenharmony_cimlxsw_sp_mc_mdb_mrouters_set(struct mlxsw_sp *mlxsw_sp, 199962306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 200062306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry, bool add) 200162306a36Sopenharmony_ci{ 200262306a36Sopenharmony_ci struct mlxsw_sp_ports_bitmap ports_bm; 200362306a36Sopenharmony_ci int err; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci err = mlxsw_sp_port_bitmap_init(mlxsw_sp, &ports_bm); 200662306a36Sopenharmony_ci if (err) 200762306a36Sopenharmony_ci return err; 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci mlxsw_sp_mc_get_mrouters_bitmap(&ports_bm, bridge_device, mlxsw_sp); 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci if (add) 201262306a36Sopenharmony_ci err = mlxsw_sp_mc_mdb_mrouters_add(mlxsw_sp, &ports_bm, 201362306a36Sopenharmony_ci mdb_entry); 201462306a36Sopenharmony_ci else 201562306a36Sopenharmony_ci mlxsw_sp_mc_mdb_mrouters_del(mlxsw_sp, &ports_bm, mdb_entry); 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci mlxsw_sp_port_bitmap_fini(&ports_bm); 201862306a36Sopenharmony_ci return err; 201962306a36Sopenharmony_ci} 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_cistatic struct mlxsw_sp_mdb_entry * 202262306a36Sopenharmony_cimlxsw_sp_mc_mdb_entry_init(struct mlxsw_sp *mlxsw_sp, 202362306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 202462306a36Sopenharmony_ci const unsigned char *addr, u16 fid, u16 local_port) 202562306a36Sopenharmony_ci{ 202662306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry_port *mdb_entry_port; 202762306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry; 202862306a36Sopenharmony_ci int err; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL); 203162306a36Sopenharmony_ci if (!mdb_entry) 203262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci ether_addr_copy(mdb_entry->key.addr, addr); 203562306a36Sopenharmony_ci mdb_entry->key.fid = fid; 203662306a36Sopenharmony_ci err = mlxsw_sp_pgt_mid_alloc(mlxsw_sp, &mdb_entry->mid); 203762306a36Sopenharmony_ci if (err) 203862306a36Sopenharmony_ci goto err_pgt_mid_alloc; 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci INIT_LIST_HEAD(&mdb_entry->ports_list); 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci err = mlxsw_sp_mc_mdb_mrouters_set(mlxsw_sp, bridge_device, mdb_entry, 204362306a36Sopenharmony_ci true); 204462306a36Sopenharmony_ci if (err) 204562306a36Sopenharmony_ci goto err_mdb_mrouters_set; 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci mdb_entry_port = mlxsw_sp_mdb_entry_port_get(mlxsw_sp, mdb_entry, 204862306a36Sopenharmony_ci local_port); 204962306a36Sopenharmony_ci if (IS_ERR(mdb_entry_port)) { 205062306a36Sopenharmony_ci err = PTR_ERR(mdb_entry_port); 205162306a36Sopenharmony_ci goto err_mdb_entry_port_get; 205262306a36Sopenharmony_ci } 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci if (bridge_device->multicast_enabled) { 205562306a36Sopenharmony_ci err = mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, true); 205662306a36Sopenharmony_ci if (err) 205762306a36Sopenharmony_ci goto err_mdb_entry_write; 205862306a36Sopenharmony_ci } 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci err = rhashtable_insert_fast(&bridge_device->mdb_ht, 206162306a36Sopenharmony_ci &mdb_entry->ht_node, 206262306a36Sopenharmony_ci mlxsw_sp_mdb_ht_params); 206362306a36Sopenharmony_ci if (err) 206462306a36Sopenharmony_ci goto err_rhashtable_insert; 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci list_add_tail(&mdb_entry->list, &bridge_device->mdb_list); 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci return mdb_entry; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_cierr_rhashtable_insert: 207162306a36Sopenharmony_ci if (bridge_device->multicast_enabled) 207262306a36Sopenharmony_ci mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, false); 207362306a36Sopenharmony_cierr_mdb_entry_write: 207462306a36Sopenharmony_ci mlxsw_sp_mdb_entry_port_put(mlxsw_sp, mdb_entry, local_port, false); 207562306a36Sopenharmony_cierr_mdb_entry_port_get: 207662306a36Sopenharmony_ci mlxsw_sp_mc_mdb_mrouters_set(mlxsw_sp, bridge_device, mdb_entry, false); 207762306a36Sopenharmony_cierr_mdb_mrouters_set: 207862306a36Sopenharmony_ci mlxsw_sp_pgt_mid_free(mlxsw_sp, mdb_entry->mid); 207962306a36Sopenharmony_cierr_pgt_mid_alloc: 208062306a36Sopenharmony_ci kfree(mdb_entry); 208162306a36Sopenharmony_ci return ERR_PTR(err); 208262306a36Sopenharmony_ci} 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_cistatic void 208562306a36Sopenharmony_cimlxsw_sp_mc_mdb_entry_fini(struct mlxsw_sp *mlxsw_sp, 208662306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry, 208762306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 208862306a36Sopenharmony_ci u16 local_port, bool force) 208962306a36Sopenharmony_ci{ 209062306a36Sopenharmony_ci list_del(&mdb_entry->list); 209162306a36Sopenharmony_ci rhashtable_remove_fast(&bridge_device->mdb_ht, &mdb_entry->ht_node, 209262306a36Sopenharmony_ci mlxsw_sp_mdb_ht_params); 209362306a36Sopenharmony_ci if (bridge_device->multicast_enabled) 209462306a36Sopenharmony_ci mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, false); 209562306a36Sopenharmony_ci mlxsw_sp_mdb_entry_port_put(mlxsw_sp, mdb_entry, local_port, force); 209662306a36Sopenharmony_ci mlxsw_sp_mc_mdb_mrouters_set(mlxsw_sp, bridge_device, mdb_entry, false); 209762306a36Sopenharmony_ci WARN_ON(!list_empty(&mdb_entry->ports_list)); 209862306a36Sopenharmony_ci mlxsw_sp_pgt_mid_free(mlxsw_sp, mdb_entry->mid); 209962306a36Sopenharmony_ci kfree(mdb_entry); 210062306a36Sopenharmony_ci} 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_cistatic struct mlxsw_sp_mdb_entry * 210362306a36Sopenharmony_cimlxsw_sp_mc_mdb_entry_get(struct mlxsw_sp *mlxsw_sp, 210462306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 210562306a36Sopenharmony_ci const unsigned char *addr, u16 fid, u16 local_port) 210662306a36Sopenharmony_ci{ 210762306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry_key key = {}; 210862306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci ether_addr_copy(key.addr, addr); 211162306a36Sopenharmony_ci key.fid = fid; 211262306a36Sopenharmony_ci mdb_entry = rhashtable_lookup_fast(&bridge_device->mdb_ht, &key, 211362306a36Sopenharmony_ci mlxsw_sp_mdb_ht_params); 211462306a36Sopenharmony_ci if (mdb_entry) { 211562306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry_port *mdb_entry_port; 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci mdb_entry_port = mlxsw_sp_mdb_entry_port_get(mlxsw_sp, 211862306a36Sopenharmony_ci mdb_entry, 211962306a36Sopenharmony_ci local_port); 212062306a36Sopenharmony_ci if (IS_ERR(mdb_entry_port)) 212162306a36Sopenharmony_ci return ERR_CAST(mdb_entry_port); 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci return mdb_entry; 212462306a36Sopenharmony_ci } 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci return mlxsw_sp_mc_mdb_entry_init(mlxsw_sp, bridge_device, addr, fid, 212762306a36Sopenharmony_ci local_port); 212862306a36Sopenharmony_ci} 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_cistatic bool 213162306a36Sopenharmony_cimlxsw_sp_mc_mdb_entry_remove(struct mlxsw_sp_mdb_entry *mdb_entry, 213262306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry_port *removed_entry_port, 213362306a36Sopenharmony_ci bool force) 213462306a36Sopenharmony_ci{ 213562306a36Sopenharmony_ci if (mdb_entry->ports_count > 1) 213662306a36Sopenharmony_ci return false; 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci if (force) 213962306a36Sopenharmony_ci return true; 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci if (!removed_entry_port->mrouter && 214262306a36Sopenharmony_ci refcount_read(&removed_entry_port->refcount) > 1) 214362306a36Sopenharmony_ci return false; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci if (removed_entry_port->mrouter && 214662306a36Sopenharmony_ci refcount_read(&removed_entry_port->refcount) > 2) 214762306a36Sopenharmony_ci return false; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci return true; 215062306a36Sopenharmony_ci} 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_cistatic void 215362306a36Sopenharmony_cimlxsw_sp_mc_mdb_entry_put(struct mlxsw_sp *mlxsw_sp, 215462306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 215562306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry, u16 local_port, 215662306a36Sopenharmony_ci bool force) 215762306a36Sopenharmony_ci{ 215862306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry_port *mdb_entry_port; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port); 216162306a36Sopenharmony_ci if (!mdb_entry_port) 216262306a36Sopenharmony_ci return; 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci /* Avoid a temporary situation in which the MDB entry points to an empty 216562306a36Sopenharmony_ci * PGT entry, as otherwise packets will be temporarily dropped instead 216662306a36Sopenharmony_ci * of being flooded. Instead, in this situation, call 216762306a36Sopenharmony_ci * mlxsw_sp_mc_mdb_entry_fini(), which first deletes the MDB entry and 216862306a36Sopenharmony_ci * then releases the PGT entry. 216962306a36Sopenharmony_ci */ 217062306a36Sopenharmony_ci if (mlxsw_sp_mc_mdb_entry_remove(mdb_entry, mdb_entry_port, force)) 217162306a36Sopenharmony_ci mlxsw_sp_mc_mdb_entry_fini(mlxsw_sp, mdb_entry, bridge_device, 217262306a36Sopenharmony_ci local_port, force); 217362306a36Sopenharmony_ci else 217462306a36Sopenharmony_ci mlxsw_sp_mdb_entry_port_put(mlxsw_sp, mdb_entry, local_port, 217562306a36Sopenharmony_ci force); 217662306a36Sopenharmony_ci} 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_cistatic int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port, 217962306a36Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb) 218062306a36Sopenharmony_ci{ 218162306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 218262306a36Sopenharmony_ci struct net_device *orig_dev = mdb->obj.orig_dev; 218362306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 218462306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 218562306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 218662306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry; 218762306a36Sopenharmony_ci u16 fid_index; 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); 219062306a36Sopenharmony_ci if (!bridge_port) 219162306a36Sopenharmony_ci return 0; 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci bridge_device = bridge_port->bridge_device; 219462306a36Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port, 219562306a36Sopenharmony_ci bridge_device, 219662306a36Sopenharmony_ci mdb->vid); 219762306a36Sopenharmony_ci if (!mlxsw_sp_port_vlan) 219862306a36Sopenharmony_ci return 0; 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci mdb_entry = mlxsw_sp_mc_mdb_entry_get(mlxsw_sp, bridge_device, 220362306a36Sopenharmony_ci mdb->addr, fid_index, 220462306a36Sopenharmony_ci mlxsw_sp_port->local_port); 220562306a36Sopenharmony_ci if (IS_ERR(mdb_entry)) 220662306a36Sopenharmony_ci return PTR_ERR(mdb_entry); 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci return 0; 220962306a36Sopenharmony_ci} 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_cistatic int 221262306a36Sopenharmony_cimlxsw_sp_bridge_mdb_mc_enable_sync(struct mlxsw_sp *mlxsw_sp, 221362306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 221462306a36Sopenharmony_ci bool mc_enabled) 221562306a36Sopenharmony_ci{ 221662306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry; 221762306a36Sopenharmony_ci int err; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) { 222062306a36Sopenharmony_ci err = mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, mc_enabled); 222162306a36Sopenharmony_ci if (err) 222262306a36Sopenharmony_ci goto err_mdb_entry_write; 222362306a36Sopenharmony_ci } 222462306a36Sopenharmony_ci return 0; 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_cierr_mdb_entry_write: 222762306a36Sopenharmony_ci list_for_each_entry_continue_reverse(mdb_entry, 222862306a36Sopenharmony_ci &bridge_device->mdb_list, list) 222962306a36Sopenharmony_ci mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, !mc_enabled); 223062306a36Sopenharmony_ci return err; 223162306a36Sopenharmony_ci} 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_cistatic void 223462306a36Sopenharmony_cimlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port, 223562306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 223662306a36Sopenharmony_ci bool add) 223762306a36Sopenharmony_ci{ 223862306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 223962306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 224062306a36Sopenharmony_ci u16 local_port = mlxsw_sp_port->local_port; 224162306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry; 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci bridge_device = bridge_port->bridge_device; 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) { 224662306a36Sopenharmony_ci if (add) 224762306a36Sopenharmony_ci mlxsw_sp_mdb_entry_mrouter_port_get(mlxsw_sp, mdb_entry, 224862306a36Sopenharmony_ci local_port); 224962306a36Sopenharmony_ci else 225062306a36Sopenharmony_ci mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, mdb_entry, 225162306a36Sopenharmony_ci local_port); 225262306a36Sopenharmony_ci } 225362306a36Sopenharmony_ci} 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_cistatic int mlxsw_sp_port_obj_add(struct net_device *dev, const void *ctx, 225662306a36Sopenharmony_ci const struct switchdev_obj *obj, 225762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 225862306a36Sopenharmony_ci{ 225962306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 226062306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan; 226162306a36Sopenharmony_ci int err = 0; 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci switch (obj->id) { 226462306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 226562306a36Sopenharmony_ci vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, extack); 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci /* The event is emitted before the changes are actually 227062306a36Sopenharmony_ci * applied to the bridge. Therefore schedule the respin 227162306a36Sopenharmony_ci * call for later, so that the respin logic sees the 227262306a36Sopenharmony_ci * updated bridge state. 227362306a36Sopenharmony_ci */ 227462306a36Sopenharmony_ci mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp); 227562306a36Sopenharmony_ci break; 227662306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_MDB: 227762306a36Sopenharmony_ci err = mlxsw_sp_port_mdb_add(mlxsw_sp_port, 227862306a36Sopenharmony_ci SWITCHDEV_OBJ_PORT_MDB(obj)); 227962306a36Sopenharmony_ci break; 228062306a36Sopenharmony_ci default: 228162306a36Sopenharmony_ci err = -EOPNOTSUPP; 228262306a36Sopenharmony_ci break; 228362306a36Sopenharmony_ci } 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci return err; 228662306a36Sopenharmony_ci} 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_cistatic void 228962306a36Sopenharmony_cimlxsw_sp_bridge_port_vlan_del(struct mlxsw_sp_port *mlxsw_sp_port, 229062306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, u16 vid) 229162306a36Sopenharmony_ci{ 229262306a36Sopenharmony_ci u16 pvid = mlxsw_sp_port->pvid == vid ? 0 : mlxsw_sp_port->pvid; 229362306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 229462306a36Sopenharmony_ci u16 proto; 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid); 229762306a36Sopenharmony_ci if (WARN_ON(!mlxsw_sp_port_vlan)) 229862306a36Sopenharmony_ci return; 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan); 230162306a36Sopenharmony_ci br_vlan_get_proto(bridge_port->bridge_device->dev, &proto); 230262306a36Sopenharmony_ci mlxsw_sp_port_pvid_set(mlxsw_sp_port, pvid, proto); 230362306a36Sopenharmony_ci mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); 230462306a36Sopenharmony_ci mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan); 230562306a36Sopenharmony_ci} 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_cistatic int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, 230862306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 230962306a36Sopenharmony_ci{ 231062306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 231162306a36Sopenharmony_ci struct net_device *orig_dev = vlan->obj.orig_dev; 231262306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci if (netif_is_bridge_master(orig_dev)) 231562306a36Sopenharmony_ci return -EOPNOTSUPP; 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); 231862306a36Sopenharmony_ci if (WARN_ON(!bridge_port)) 231962306a36Sopenharmony_ci return -EINVAL; 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci if (!bridge_port->bridge_device->vlan_enabled) 232262306a36Sopenharmony_ci return 0; 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci mlxsw_sp_bridge_port_vlan_del(mlxsw_sp_port, bridge_port, vlan->vid); 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ci return 0; 232762306a36Sopenharmony_ci} 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_cistatic int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port, 233062306a36Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb) 233162306a36Sopenharmony_ci{ 233262306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 233362306a36Sopenharmony_ci struct net_device *orig_dev = mdb->obj.orig_dev; 233462306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 233562306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 233662306a36Sopenharmony_ci struct net_device *dev = mlxsw_sp_port->dev; 233762306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 233862306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry_key key = {}; 233962306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry; 234062306a36Sopenharmony_ci u16 fid_index; 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); 234362306a36Sopenharmony_ci if (!bridge_port) 234462306a36Sopenharmony_ci return 0; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci bridge_device = bridge_port->bridge_device; 234762306a36Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port, 234862306a36Sopenharmony_ci bridge_device, 234962306a36Sopenharmony_ci mdb->vid); 235062306a36Sopenharmony_ci if (!mlxsw_sp_port_vlan) 235162306a36Sopenharmony_ci return 0; 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci ether_addr_copy(key.addr, mdb->addr); 235662306a36Sopenharmony_ci key.fid = fid_index; 235762306a36Sopenharmony_ci mdb_entry = rhashtable_lookup_fast(&bridge_device->mdb_ht, &key, 235862306a36Sopenharmony_ci mlxsw_sp_mdb_ht_params); 235962306a36Sopenharmony_ci if (!mdb_entry) { 236062306a36Sopenharmony_ci netdev_err(dev, "Unable to remove port from MC DB\n"); 236162306a36Sopenharmony_ci return -EINVAL; 236262306a36Sopenharmony_ci } 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci mlxsw_sp_mc_mdb_entry_put(mlxsw_sp, bridge_device, mdb_entry, 236562306a36Sopenharmony_ci mlxsw_sp_port->local_port, false); 236662306a36Sopenharmony_ci return 0; 236762306a36Sopenharmony_ci} 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_cistatic void 237062306a36Sopenharmony_cimlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, 237162306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 237262306a36Sopenharmony_ci u16 fid_index) 237362306a36Sopenharmony_ci{ 237462306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 237562306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 237662306a36Sopenharmony_ci struct mlxsw_sp_mdb_entry *mdb_entry, *tmp; 237762306a36Sopenharmony_ci u16 local_port = mlxsw_sp_port->local_port; 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci bridge_device = bridge_port->bridge_device; 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci list_for_each_entry_safe(mdb_entry, tmp, &bridge_device->mdb_list, 238262306a36Sopenharmony_ci list) { 238362306a36Sopenharmony_ci if (mdb_entry->key.fid != fid_index) 238462306a36Sopenharmony_ci continue; 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci if (bridge_port->mrouter) 238762306a36Sopenharmony_ci mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, 238862306a36Sopenharmony_ci mdb_entry, 238962306a36Sopenharmony_ci local_port); 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci mlxsw_sp_mc_mdb_entry_put(mlxsw_sp, bridge_device, mdb_entry, 239262306a36Sopenharmony_ci local_port, true); 239362306a36Sopenharmony_ci } 239462306a36Sopenharmony_ci} 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_cistatic int mlxsw_sp_port_obj_del(struct net_device *dev, const void *ctx, 239762306a36Sopenharmony_ci const struct switchdev_obj *obj) 239862306a36Sopenharmony_ci{ 239962306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 240062306a36Sopenharmony_ci int err = 0; 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci switch (obj->id) { 240362306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 240462306a36Sopenharmony_ci err = mlxsw_sp_port_vlans_del(mlxsw_sp_port, 240562306a36Sopenharmony_ci SWITCHDEV_OBJ_PORT_VLAN(obj)); 240662306a36Sopenharmony_ci break; 240762306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_MDB: 240862306a36Sopenharmony_ci err = mlxsw_sp_port_mdb_del(mlxsw_sp_port, 240962306a36Sopenharmony_ci SWITCHDEV_OBJ_PORT_MDB(obj)); 241062306a36Sopenharmony_ci break; 241162306a36Sopenharmony_ci default: 241262306a36Sopenharmony_ci err = -EOPNOTSUPP; 241362306a36Sopenharmony_ci break; 241462306a36Sopenharmony_ci } 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp); 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci return err; 241962306a36Sopenharmony_ci} 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_cistatic struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp, 242262306a36Sopenharmony_ci u16 lag_id) 242362306a36Sopenharmony_ci{ 242462306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 242562306a36Sopenharmony_ci u64 max_lag_members; 242662306a36Sopenharmony_ci int i; 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci max_lag_members = MLXSW_CORE_RES_GET(mlxsw_sp->core, 242962306a36Sopenharmony_ci MAX_LAG_MEMBERS); 243062306a36Sopenharmony_ci for (i = 0; i < max_lag_members; i++) { 243162306a36Sopenharmony_ci mlxsw_sp_port = mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i); 243262306a36Sopenharmony_ci if (mlxsw_sp_port) 243362306a36Sopenharmony_ci return mlxsw_sp_port; 243462306a36Sopenharmony_ci } 243562306a36Sopenharmony_ci return NULL; 243662306a36Sopenharmony_ci} 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_cistatic int 243962306a36Sopenharmony_cimlxsw_sp_bridge_port_replay(struct mlxsw_sp_bridge_port *bridge_port, 244062306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 244162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 244262306a36Sopenharmony_ci{ 244362306a36Sopenharmony_ci struct mlxsw_sp_bridge_port_replay_switchdev_objs rso = { 244462306a36Sopenharmony_ci .brport_dev = bridge_port->dev, 244562306a36Sopenharmony_ci .mlxsw_sp_port = mlxsw_sp_port, 244662306a36Sopenharmony_ci }; 244762306a36Sopenharmony_ci struct notifier_block *nb; 244862306a36Sopenharmony_ci int err; 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci nb = &mlxsw_sp_bridge_port_replay_switchdev_objs_nb; 245162306a36Sopenharmony_ci err = switchdev_bridge_port_replay(bridge_port->dev, mlxsw_sp_port->dev, 245262306a36Sopenharmony_ci &rso, NULL, nb, extack); 245362306a36Sopenharmony_ci if (err) 245462306a36Sopenharmony_ci goto err_replay; 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci return 0; 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_cierr_replay: 245962306a36Sopenharmony_ci nb = &mlxsw_sp_bridge_port_unreplay_switchdev_objs_nb; 246062306a36Sopenharmony_ci switchdev_bridge_port_replay(bridge_port->dev, mlxsw_sp_port->dev, 246162306a36Sopenharmony_ci &rso, NULL, nb, extack); 246262306a36Sopenharmony_ci return err; 246362306a36Sopenharmony_ci} 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_cistatic int 246662306a36Sopenharmony_cimlxsw_sp_bridge_vlan_aware_port_join(struct mlxsw_sp_bridge_port *bridge_port, 246762306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 246862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 246962306a36Sopenharmony_ci{ 247062306a36Sopenharmony_ci if (is_vlan_dev(bridge_port->dev)) { 247162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Can not enslave a VLAN device to a VLAN-aware bridge"); 247262306a36Sopenharmony_ci return -EINVAL; 247362306a36Sopenharmony_ci } 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci /* Port is no longer usable as a router interface */ 247662306a36Sopenharmony_ci if (mlxsw_sp_port->default_vlan->fid) 247762306a36Sopenharmony_ci mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port->default_vlan); 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci return mlxsw_sp_bridge_port_replay(bridge_port, mlxsw_sp_port, extack); 248062306a36Sopenharmony_ci} 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_cistatic int 248362306a36Sopenharmony_cimlxsw_sp_bridge_8021q_port_join(struct mlxsw_sp_bridge_device *bridge_device, 248462306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 248562306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 248662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 248762306a36Sopenharmony_ci{ 248862306a36Sopenharmony_ci return mlxsw_sp_bridge_vlan_aware_port_join(bridge_port, mlxsw_sp_port, 248962306a36Sopenharmony_ci extack); 249062306a36Sopenharmony_ci} 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_cistatic void 249362306a36Sopenharmony_cimlxsw_sp_bridge_vlan_aware_port_leave(struct mlxsw_sp_port *mlxsw_sp_port) 249462306a36Sopenharmony_ci{ 249562306a36Sopenharmony_ci /* Make sure untagged frames are allowed to ingress */ 249662306a36Sopenharmony_ci mlxsw_sp_port_pvid_set(mlxsw_sp_port, MLXSW_SP_DEFAULT_VID, 249762306a36Sopenharmony_ci ETH_P_8021Q); 249862306a36Sopenharmony_ci} 249962306a36Sopenharmony_ci 250062306a36Sopenharmony_cistatic void 250162306a36Sopenharmony_cimlxsw_sp_bridge_8021q_port_leave(struct mlxsw_sp_bridge_device *bridge_device, 250262306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 250362306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port) 250462306a36Sopenharmony_ci{ 250562306a36Sopenharmony_ci mlxsw_sp_bridge_vlan_aware_port_leave(mlxsw_sp_port); 250662306a36Sopenharmony_ci} 250762306a36Sopenharmony_ci 250862306a36Sopenharmony_cistatic int 250962306a36Sopenharmony_cimlxsw_sp_bridge_vlan_aware_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device, 251062306a36Sopenharmony_ci const struct net_device *vxlan_dev, 251162306a36Sopenharmony_ci u16 vid, u16 ethertype, 251262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 251362306a36Sopenharmony_ci{ 251462306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); 251562306a36Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); 251662306a36Sopenharmony_ci struct mlxsw_sp_nve_params params = { 251762306a36Sopenharmony_ci .type = MLXSW_SP_NVE_TYPE_VXLAN, 251862306a36Sopenharmony_ci .vni = vxlan->cfg.vni, 251962306a36Sopenharmony_ci .dev = vxlan_dev, 252062306a36Sopenharmony_ci .ethertype = ethertype, 252162306a36Sopenharmony_ci }; 252262306a36Sopenharmony_ci struct mlxsw_sp_fid *fid; 252362306a36Sopenharmony_ci int err; 252462306a36Sopenharmony_ci 252562306a36Sopenharmony_ci /* If the VLAN is 0, we need to find the VLAN that is configured as 252662306a36Sopenharmony_ci * PVID and egress untagged on the bridge port of the VxLAN device. 252762306a36Sopenharmony_ci * It is possible no such VLAN exists 252862306a36Sopenharmony_ci */ 252962306a36Sopenharmony_ci if (!vid) { 253062306a36Sopenharmony_ci err = mlxsw_sp_vxlan_mapped_vid(vxlan_dev, &vid); 253162306a36Sopenharmony_ci if (err || !vid) 253262306a36Sopenharmony_ci return err; 253362306a36Sopenharmony_ci } 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci fid = mlxsw_sp_fid_8021q_get(mlxsw_sp, vid); 253662306a36Sopenharmony_ci if (IS_ERR(fid)) { 253762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1Q FID"); 253862306a36Sopenharmony_ci return PTR_ERR(fid); 253962306a36Sopenharmony_ci } 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci if (mlxsw_sp_fid_vni_is_set(fid)) { 254262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VNI is already set on FID"); 254362306a36Sopenharmony_ci err = -EINVAL; 254462306a36Sopenharmony_ci goto err_vni_exists; 254562306a36Sopenharmony_ci } 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_ci err = mlxsw_sp_nve_fid_enable(mlxsw_sp, fid, ¶ms, extack); 254862306a36Sopenharmony_ci if (err) 254962306a36Sopenharmony_ci goto err_nve_fid_enable; 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci return 0; 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_cierr_nve_fid_enable: 255462306a36Sopenharmony_cierr_vni_exists: 255562306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 255662306a36Sopenharmony_ci return err; 255762306a36Sopenharmony_ci} 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_cistatic int 256062306a36Sopenharmony_cimlxsw_sp_bridge_8021q_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device, 256162306a36Sopenharmony_ci const struct net_device *vxlan_dev, u16 vid, 256262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 256362306a36Sopenharmony_ci{ 256462306a36Sopenharmony_ci return mlxsw_sp_bridge_vlan_aware_vxlan_join(bridge_device, vxlan_dev, 256562306a36Sopenharmony_ci vid, ETH_P_8021Q, extack); 256662306a36Sopenharmony_ci} 256762306a36Sopenharmony_ci 256862306a36Sopenharmony_cistatic struct net_device * 256962306a36Sopenharmony_cimlxsw_sp_bridge_8021q_vxlan_dev_find(struct net_device *br_dev, u16 vid) 257062306a36Sopenharmony_ci{ 257162306a36Sopenharmony_ci struct net_device *dev; 257262306a36Sopenharmony_ci struct list_head *iter; 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci netdev_for_each_lower_dev(br_dev, dev, iter) { 257562306a36Sopenharmony_ci u16 pvid; 257662306a36Sopenharmony_ci int err; 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci if (!netif_is_vxlan(dev)) 257962306a36Sopenharmony_ci continue; 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ci err = mlxsw_sp_vxlan_mapped_vid(dev, &pvid); 258262306a36Sopenharmony_ci if (err || pvid != vid) 258362306a36Sopenharmony_ci continue; 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_ci return dev; 258662306a36Sopenharmony_ci } 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci return NULL; 258962306a36Sopenharmony_ci} 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_cistatic struct mlxsw_sp_fid * 259262306a36Sopenharmony_cimlxsw_sp_bridge_8021q_fid_get(struct mlxsw_sp_bridge_device *bridge_device, 259362306a36Sopenharmony_ci u16 vid, struct netlink_ext_ack *extack) 259462306a36Sopenharmony_ci{ 259562306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_ci return mlxsw_sp_fid_8021q_get(mlxsw_sp, vid); 259862306a36Sopenharmony_ci} 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_cistatic struct mlxsw_sp_fid * 260162306a36Sopenharmony_cimlxsw_sp_bridge_8021q_fid_lookup(struct mlxsw_sp_bridge_device *bridge_device, 260262306a36Sopenharmony_ci u16 vid) 260362306a36Sopenharmony_ci{ 260462306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); 260562306a36Sopenharmony_ci 260662306a36Sopenharmony_ci return mlxsw_sp_fid_8021q_lookup(mlxsw_sp, vid); 260762306a36Sopenharmony_ci} 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_cistatic u16 261062306a36Sopenharmony_cimlxsw_sp_bridge_8021q_fid_vid(struct mlxsw_sp_bridge_device *bridge_device, 261162306a36Sopenharmony_ci const struct mlxsw_sp_fid *fid) 261262306a36Sopenharmony_ci{ 261362306a36Sopenharmony_ci return mlxsw_sp_fid_8021q_vid(fid); 261462306a36Sopenharmony_ci} 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_cistatic const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021q_ops = { 261762306a36Sopenharmony_ci .port_join = mlxsw_sp_bridge_8021q_port_join, 261862306a36Sopenharmony_ci .port_leave = mlxsw_sp_bridge_8021q_port_leave, 261962306a36Sopenharmony_ci .vxlan_join = mlxsw_sp_bridge_8021q_vxlan_join, 262062306a36Sopenharmony_ci .fid_get = mlxsw_sp_bridge_8021q_fid_get, 262162306a36Sopenharmony_ci .fid_lookup = mlxsw_sp_bridge_8021q_fid_lookup, 262262306a36Sopenharmony_ci .fid_vid = mlxsw_sp_bridge_8021q_fid_vid, 262362306a36Sopenharmony_ci}; 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_cistatic bool 262662306a36Sopenharmony_cimlxsw_sp_port_is_br_member(const struct mlxsw_sp_port *mlxsw_sp_port, 262762306a36Sopenharmony_ci const struct net_device *br_dev) 262862306a36Sopenharmony_ci{ 262962306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list, 263262306a36Sopenharmony_ci list) { 263362306a36Sopenharmony_ci if (mlxsw_sp_port_vlan->bridge_port && 263462306a36Sopenharmony_ci mlxsw_sp_port_vlan->bridge_port->bridge_device->dev == 263562306a36Sopenharmony_ci br_dev) 263662306a36Sopenharmony_ci return true; 263762306a36Sopenharmony_ci } 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci return false; 264062306a36Sopenharmony_ci} 264162306a36Sopenharmony_ci 264262306a36Sopenharmony_cistatic int 264362306a36Sopenharmony_cimlxsw_sp_bridge_8021d_port_join(struct mlxsw_sp_bridge_device *bridge_device, 264462306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 264562306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 264662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 264762306a36Sopenharmony_ci{ 264862306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 264962306a36Sopenharmony_ci struct net_device *dev = bridge_port->dev; 265062306a36Sopenharmony_ci u16 vid; 265162306a36Sopenharmony_ci int err; 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : MLXSW_SP_DEFAULT_VID; 265462306a36Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid); 265562306a36Sopenharmony_ci if (WARN_ON(!mlxsw_sp_port_vlan)) 265662306a36Sopenharmony_ci return -EINVAL; 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci if (mlxsw_sp_port_is_br_member(mlxsw_sp_port, bridge_device->dev)) { 265962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Can not bridge VLAN uppers of the same port"); 266062306a36Sopenharmony_ci return -EINVAL; 266162306a36Sopenharmony_ci } 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci /* Port is no longer usable as a router interface */ 266462306a36Sopenharmony_ci if (mlxsw_sp_port_vlan->fid) 266562306a36Sopenharmony_ci mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan); 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci err = mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port, 266862306a36Sopenharmony_ci extack); 266962306a36Sopenharmony_ci if (err) 267062306a36Sopenharmony_ci return err; 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci err = mlxsw_sp_bridge_port_replay(bridge_port, mlxsw_sp_port, extack); 267362306a36Sopenharmony_ci if (err) 267462306a36Sopenharmony_ci goto err_replay; 267562306a36Sopenharmony_ci 267662306a36Sopenharmony_ci return 0; 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_cierr_replay: 267962306a36Sopenharmony_ci mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan); 268062306a36Sopenharmony_ci return err; 268162306a36Sopenharmony_ci} 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_cistatic void 268462306a36Sopenharmony_cimlxsw_sp_bridge_8021d_port_leave(struct mlxsw_sp_bridge_device *bridge_device, 268562306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 268662306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port) 268762306a36Sopenharmony_ci{ 268862306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 268962306a36Sopenharmony_ci struct net_device *dev = bridge_port->dev; 269062306a36Sopenharmony_ci u16 vid; 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_ci vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : MLXSW_SP_DEFAULT_VID; 269362306a36Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid); 269462306a36Sopenharmony_ci if (!mlxsw_sp_port_vlan || !mlxsw_sp_port_vlan->bridge_port) 269562306a36Sopenharmony_ci return; 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan); 269862306a36Sopenharmony_ci} 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_cistatic int 270162306a36Sopenharmony_cimlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device, 270262306a36Sopenharmony_ci const struct net_device *vxlan_dev, u16 vid, 270362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 270462306a36Sopenharmony_ci{ 270562306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); 270662306a36Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); 270762306a36Sopenharmony_ci struct mlxsw_sp_nve_params params = { 270862306a36Sopenharmony_ci .type = MLXSW_SP_NVE_TYPE_VXLAN, 270962306a36Sopenharmony_ci .vni = vxlan->cfg.vni, 271062306a36Sopenharmony_ci .dev = vxlan_dev, 271162306a36Sopenharmony_ci .ethertype = ETH_P_8021Q, 271262306a36Sopenharmony_ci }; 271362306a36Sopenharmony_ci struct mlxsw_sp_fid *fid; 271462306a36Sopenharmony_ci int err; 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci fid = mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex); 271762306a36Sopenharmony_ci if (IS_ERR(fid)) { 271862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1D FID"); 271962306a36Sopenharmony_ci return -EINVAL; 272062306a36Sopenharmony_ci } 272162306a36Sopenharmony_ci 272262306a36Sopenharmony_ci if (mlxsw_sp_fid_vni_is_set(fid)) { 272362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VNI is already set on FID"); 272462306a36Sopenharmony_ci err = -EINVAL; 272562306a36Sopenharmony_ci goto err_vni_exists; 272662306a36Sopenharmony_ci } 272762306a36Sopenharmony_ci 272862306a36Sopenharmony_ci err = mlxsw_sp_nve_fid_enable(mlxsw_sp, fid, ¶ms, extack); 272962306a36Sopenharmony_ci if (err) 273062306a36Sopenharmony_ci goto err_nve_fid_enable; 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci return 0; 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_cierr_nve_fid_enable: 273562306a36Sopenharmony_cierr_vni_exists: 273662306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 273762306a36Sopenharmony_ci return err; 273862306a36Sopenharmony_ci} 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_cistatic struct mlxsw_sp_fid * 274162306a36Sopenharmony_cimlxsw_sp_bridge_8021d_fid_get(struct mlxsw_sp_bridge_device *bridge_device, 274262306a36Sopenharmony_ci u16 vid, struct netlink_ext_ack *extack) 274362306a36Sopenharmony_ci{ 274462306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); 274562306a36Sopenharmony_ci 274662306a36Sopenharmony_ci return mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex); 274762306a36Sopenharmony_ci} 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_cistatic struct mlxsw_sp_fid * 275062306a36Sopenharmony_cimlxsw_sp_bridge_8021d_fid_lookup(struct mlxsw_sp_bridge_device *bridge_device, 275162306a36Sopenharmony_ci u16 vid) 275262306a36Sopenharmony_ci{ 275362306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci /* The only valid VLAN for a VLAN-unaware bridge is 0 */ 275662306a36Sopenharmony_ci if (vid) 275762306a36Sopenharmony_ci return NULL; 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci return mlxsw_sp_fid_8021d_lookup(mlxsw_sp, bridge_device->dev->ifindex); 276062306a36Sopenharmony_ci} 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_cistatic u16 276362306a36Sopenharmony_cimlxsw_sp_bridge_8021d_fid_vid(struct mlxsw_sp_bridge_device *bridge_device, 276462306a36Sopenharmony_ci const struct mlxsw_sp_fid *fid) 276562306a36Sopenharmony_ci{ 276662306a36Sopenharmony_ci return 0; 276762306a36Sopenharmony_ci} 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_cistatic const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021d_ops = { 277062306a36Sopenharmony_ci .port_join = mlxsw_sp_bridge_8021d_port_join, 277162306a36Sopenharmony_ci .port_leave = mlxsw_sp_bridge_8021d_port_leave, 277262306a36Sopenharmony_ci .vxlan_join = mlxsw_sp_bridge_8021d_vxlan_join, 277362306a36Sopenharmony_ci .fid_get = mlxsw_sp_bridge_8021d_fid_get, 277462306a36Sopenharmony_ci .fid_lookup = mlxsw_sp_bridge_8021d_fid_lookup, 277562306a36Sopenharmony_ci .fid_vid = mlxsw_sp_bridge_8021d_fid_vid, 277662306a36Sopenharmony_ci}; 277762306a36Sopenharmony_ci 277862306a36Sopenharmony_cistatic int 277962306a36Sopenharmony_cimlxsw_sp_bridge_8021ad_port_join(struct mlxsw_sp_bridge_device *bridge_device, 278062306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 278162306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 278262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 278362306a36Sopenharmony_ci{ 278462306a36Sopenharmony_ci int err; 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci err = mlxsw_sp_port_vlan_classification_set(mlxsw_sp_port, true, false); 278762306a36Sopenharmony_ci if (err) 278862306a36Sopenharmony_ci return err; 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci err = mlxsw_sp_bridge_vlan_aware_port_join(bridge_port, mlxsw_sp_port, 279162306a36Sopenharmony_ci extack); 279262306a36Sopenharmony_ci if (err) 279362306a36Sopenharmony_ci goto err_bridge_vlan_aware_port_join; 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_ci return 0; 279662306a36Sopenharmony_ci 279762306a36Sopenharmony_cierr_bridge_vlan_aware_port_join: 279862306a36Sopenharmony_ci mlxsw_sp_port_vlan_classification_set(mlxsw_sp_port, false, true); 279962306a36Sopenharmony_ci return err; 280062306a36Sopenharmony_ci} 280162306a36Sopenharmony_ci 280262306a36Sopenharmony_cistatic void 280362306a36Sopenharmony_cimlxsw_sp_bridge_8021ad_port_leave(struct mlxsw_sp_bridge_device *bridge_device, 280462306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 280562306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port) 280662306a36Sopenharmony_ci{ 280762306a36Sopenharmony_ci mlxsw_sp_bridge_vlan_aware_port_leave(mlxsw_sp_port); 280862306a36Sopenharmony_ci mlxsw_sp_port_vlan_classification_set(mlxsw_sp_port, false, true); 280962306a36Sopenharmony_ci} 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_cistatic int 281262306a36Sopenharmony_cimlxsw_sp_bridge_8021ad_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device, 281362306a36Sopenharmony_ci const struct net_device *vxlan_dev, u16 vid, 281462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 281562306a36Sopenharmony_ci{ 281662306a36Sopenharmony_ci return mlxsw_sp_bridge_vlan_aware_vxlan_join(bridge_device, vxlan_dev, 281762306a36Sopenharmony_ci vid, ETH_P_8021AD, extack); 281862306a36Sopenharmony_ci} 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_cistatic const struct mlxsw_sp_bridge_ops mlxsw_sp1_bridge_8021ad_ops = { 282162306a36Sopenharmony_ci .port_join = mlxsw_sp_bridge_8021ad_port_join, 282262306a36Sopenharmony_ci .port_leave = mlxsw_sp_bridge_8021ad_port_leave, 282362306a36Sopenharmony_ci .vxlan_join = mlxsw_sp_bridge_8021ad_vxlan_join, 282462306a36Sopenharmony_ci .fid_get = mlxsw_sp_bridge_8021q_fid_get, 282562306a36Sopenharmony_ci .fid_lookup = mlxsw_sp_bridge_8021q_fid_lookup, 282662306a36Sopenharmony_ci .fid_vid = mlxsw_sp_bridge_8021q_fid_vid, 282762306a36Sopenharmony_ci}; 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_cistatic int 283062306a36Sopenharmony_cimlxsw_sp2_bridge_8021ad_port_join(struct mlxsw_sp_bridge_device *bridge_device, 283162306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 283262306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 283362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 283462306a36Sopenharmony_ci{ 283562306a36Sopenharmony_ci int err; 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci /* The EtherType of decapsulated packets is determined at the egress 283862306a36Sopenharmony_ci * port to allow 802.1d and 802.1ad bridges with VXLAN devices to 283962306a36Sopenharmony_ci * co-exist. 284062306a36Sopenharmony_ci */ 284162306a36Sopenharmony_ci err = mlxsw_sp_port_egress_ethtype_set(mlxsw_sp_port, ETH_P_8021AD); 284262306a36Sopenharmony_ci if (err) 284362306a36Sopenharmony_ci return err; 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci err = mlxsw_sp_bridge_8021ad_port_join(bridge_device, bridge_port, 284662306a36Sopenharmony_ci mlxsw_sp_port, extack); 284762306a36Sopenharmony_ci if (err) 284862306a36Sopenharmony_ci goto err_bridge_8021ad_port_join; 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_ci return 0; 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_cierr_bridge_8021ad_port_join: 285362306a36Sopenharmony_ci mlxsw_sp_port_egress_ethtype_set(mlxsw_sp_port, ETH_P_8021Q); 285462306a36Sopenharmony_ci return err; 285562306a36Sopenharmony_ci} 285662306a36Sopenharmony_ci 285762306a36Sopenharmony_cistatic void 285862306a36Sopenharmony_cimlxsw_sp2_bridge_8021ad_port_leave(struct mlxsw_sp_bridge_device *bridge_device, 285962306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port, 286062306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port) 286162306a36Sopenharmony_ci{ 286262306a36Sopenharmony_ci mlxsw_sp_bridge_8021ad_port_leave(bridge_device, bridge_port, 286362306a36Sopenharmony_ci mlxsw_sp_port); 286462306a36Sopenharmony_ci mlxsw_sp_port_egress_ethtype_set(mlxsw_sp_port, ETH_P_8021Q); 286562306a36Sopenharmony_ci} 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_cistatic const struct mlxsw_sp_bridge_ops mlxsw_sp2_bridge_8021ad_ops = { 286862306a36Sopenharmony_ci .port_join = mlxsw_sp2_bridge_8021ad_port_join, 286962306a36Sopenharmony_ci .port_leave = mlxsw_sp2_bridge_8021ad_port_leave, 287062306a36Sopenharmony_ci .vxlan_join = mlxsw_sp_bridge_8021ad_vxlan_join, 287162306a36Sopenharmony_ci .fid_get = mlxsw_sp_bridge_8021q_fid_get, 287262306a36Sopenharmony_ci .fid_lookup = mlxsw_sp_bridge_8021q_fid_lookup, 287362306a36Sopenharmony_ci .fid_vid = mlxsw_sp_bridge_8021q_fid_vid, 287462306a36Sopenharmony_ci}; 287562306a36Sopenharmony_ci 287662306a36Sopenharmony_ciint mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port, 287762306a36Sopenharmony_ci struct net_device *brport_dev, 287862306a36Sopenharmony_ci struct net_device *br_dev, 287962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 288062306a36Sopenharmony_ci{ 288162306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 288262306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 288362306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 288462306a36Sopenharmony_ci int err; 288562306a36Sopenharmony_ci 288662306a36Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_get(mlxsw_sp->bridge, brport_dev, 288762306a36Sopenharmony_ci extack); 288862306a36Sopenharmony_ci if (IS_ERR(bridge_port)) 288962306a36Sopenharmony_ci return PTR_ERR(bridge_port); 289062306a36Sopenharmony_ci bridge_device = bridge_port->bridge_device; 289162306a36Sopenharmony_ci 289262306a36Sopenharmony_ci err = bridge_device->ops->port_join(bridge_device, bridge_port, 289362306a36Sopenharmony_ci mlxsw_sp_port, extack); 289462306a36Sopenharmony_ci if (err) 289562306a36Sopenharmony_ci goto err_port_join; 289662306a36Sopenharmony_ci 289762306a36Sopenharmony_ci err = mlxsw_sp_netdevice_enslavement_replay(mlxsw_sp, br_dev, extack); 289862306a36Sopenharmony_ci if (err) 289962306a36Sopenharmony_ci goto err_replay; 290062306a36Sopenharmony_ci 290162306a36Sopenharmony_ci return 0; 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_cierr_replay: 290462306a36Sopenharmony_ci bridge_device->ops->port_leave(bridge_device, bridge_port, 290562306a36Sopenharmony_ci mlxsw_sp_port); 290662306a36Sopenharmony_cierr_port_join: 290762306a36Sopenharmony_ci mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port); 290862306a36Sopenharmony_ci return err; 290962306a36Sopenharmony_ci} 291062306a36Sopenharmony_ci 291162306a36Sopenharmony_civoid mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port, 291262306a36Sopenharmony_ci struct net_device *brport_dev, 291362306a36Sopenharmony_ci struct net_device *br_dev) 291462306a36Sopenharmony_ci{ 291562306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 291662306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 291762306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 291862306a36Sopenharmony_ci 291962306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 292062306a36Sopenharmony_ci if (!bridge_device) 292162306a36Sopenharmony_ci return; 292262306a36Sopenharmony_ci bridge_port = __mlxsw_sp_bridge_port_find(bridge_device, brport_dev); 292362306a36Sopenharmony_ci if (!bridge_port) 292462306a36Sopenharmony_ci return; 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ci bridge_device->ops->port_leave(bridge_device, bridge_port, 292762306a36Sopenharmony_ci mlxsw_sp_port); 292862306a36Sopenharmony_ci mlxsw_sp_port_security_set(mlxsw_sp_port, false); 292962306a36Sopenharmony_ci mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port); 293062306a36Sopenharmony_ci} 293162306a36Sopenharmony_ci 293262306a36Sopenharmony_ciint mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp, 293362306a36Sopenharmony_ci const struct net_device *br_dev, 293462306a36Sopenharmony_ci const struct net_device *vxlan_dev, u16 vid, 293562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 293662306a36Sopenharmony_ci{ 293762306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 293862306a36Sopenharmony_ci 293962306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 294062306a36Sopenharmony_ci if (WARN_ON(!bridge_device)) 294162306a36Sopenharmony_ci return -EINVAL; 294262306a36Sopenharmony_ci 294362306a36Sopenharmony_ci return bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, vid, 294462306a36Sopenharmony_ci extack); 294562306a36Sopenharmony_ci} 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_civoid mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp, 294862306a36Sopenharmony_ci const struct net_device *vxlan_dev) 294962306a36Sopenharmony_ci{ 295062306a36Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); 295162306a36Sopenharmony_ci struct mlxsw_sp_fid *fid; 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci /* If the VxLAN device is down, then the FID does not have a VNI */ 295462306a36Sopenharmony_ci fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan->cfg.vni); 295562306a36Sopenharmony_ci if (!fid) 295662306a36Sopenharmony_ci return; 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_ci mlxsw_sp_nve_fid_disable(mlxsw_sp, fid); 295962306a36Sopenharmony_ci /* Drop both the reference we just took during lookup and the reference 296062306a36Sopenharmony_ci * the VXLAN device took. 296162306a36Sopenharmony_ci */ 296262306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 296362306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 296462306a36Sopenharmony_ci} 296562306a36Sopenharmony_ci 296662306a36Sopenharmony_cistatic void 296762306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_addr_convert(const union vxlan_addr *vxlan_addr, 296862306a36Sopenharmony_ci enum mlxsw_sp_l3proto *proto, 296962306a36Sopenharmony_ci union mlxsw_sp_l3addr *addr) 297062306a36Sopenharmony_ci{ 297162306a36Sopenharmony_ci if (vxlan_addr->sa.sa_family == AF_INET) { 297262306a36Sopenharmony_ci addr->addr4 = vxlan_addr->sin.sin_addr.s_addr; 297362306a36Sopenharmony_ci *proto = MLXSW_SP_L3_PROTO_IPV4; 297462306a36Sopenharmony_ci } else { 297562306a36Sopenharmony_ci addr->addr6 = vxlan_addr->sin6.sin6_addr; 297662306a36Sopenharmony_ci *proto = MLXSW_SP_L3_PROTO_IPV6; 297762306a36Sopenharmony_ci } 297862306a36Sopenharmony_ci} 297962306a36Sopenharmony_ci 298062306a36Sopenharmony_cistatic void 298162306a36Sopenharmony_cimlxsw_sp_switchdev_addr_vxlan_convert(enum mlxsw_sp_l3proto proto, 298262306a36Sopenharmony_ci const union mlxsw_sp_l3addr *addr, 298362306a36Sopenharmony_ci union vxlan_addr *vxlan_addr) 298462306a36Sopenharmony_ci{ 298562306a36Sopenharmony_ci switch (proto) { 298662306a36Sopenharmony_ci case MLXSW_SP_L3_PROTO_IPV4: 298762306a36Sopenharmony_ci vxlan_addr->sa.sa_family = AF_INET; 298862306a36Sopenharmony_ci vxlan_addr->sin.sin_addr.s_addr = addr->addr4; 298962306a36Sopenharmony_ci break; 299062306a36Sopenharmony_ci case MLXSW_SP_L3_PROTO_IPV6: 299162306a36Sopenharmony_ci vxlan_addr->sa.sa_family = AF_INET6; 299262306a36Sopenharmony_ci vxlan_addr->sin6.sin6_addr = addr->addr6; 299362306a36Sopenharmony_ci break; 299462306a36Sopenharmony_ci } 299562306a36Sopenharmony_ci} 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_cistatic void mlxsw_sp_fdb_vxlan_call_notifiers(struct net_device *dev, 299862306a36Sopenharmony_ci const char *mac, 299962306a36Sopenharmony_ci enum mlxsw_sp_l3proto proto, 300062306a36Sopenharmony_ci union mlxsw_sp_l3addr *addr, 300162306a36Sopenharmony_ci __be32 vni, bool adding) 300262306a36Sopenharmony_ci{ 300362306a36Sopenharmony_ci struct switchdev_notifier_vxlan_fdb_info info; 300462306a36Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(dev); 300562306a36Sopenharmony_ci enum switchdev_notifier_type type; 300662306a36Sopenharmony_ci 300762306a36Sopenharmony_ci type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE : 300862306a36Sopenharmony_ci SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE; 300962306a36Sopenharmony_ci mlxsw_sp_switchdev_addr_vxlan_convert(proto, addr, &info.remote_ip); 301062306a36Sopenharmony_ci info.remote_port = vxlan->cfg.dst_port; 301162306a36Sopenharmony_ci info.remote_vni = vni; 301262306a36Sopenharmony_ci info.remote_ifindex = 0; 301362306a36Sopenharmony_ci ether_addr_copy(info.eth_addr, mac); 301462306a36Sopenharmony_ci info.vni = vni; 301562306a36Sopenharmony_ci info.offloaded = adding; 301662306a36Sopenharmony_ci call_switchdev_notifiers(type, dev, &info.info, NULL); 301762306a36Sopenharmony_ci} 301862306a36Sopenharmony_ci 301962306a36Sopenharmony_cistatic void mlxsw_sp_fdb_nve_call_notifiers(struct net_device *dev, 302062306a36Sopenharmony_ci const char *mac, 302162306a36Sopenharmony_ci enum mlxsw_sp_l3proto proto, 302262306a36Sopenharmony_ci union mlxsw_sp_l3addr *addr, 302362306a36Sopenharmony_ci __be32 vni, 302462306a36Sopenharmony_ci bool adding) 302562306a36Sopenharmony_ci{ 302662306a36Sopenharmony_ci if (netif_is_vxlan(dev)) 302762306a36Sopenharmony_ci mlxsw_sp_fdb_vxlan_call_notifiers(dev, mac, proto, addr, vni, 302862306a36Sopenharmony_ci adding); 302962306a36Sopenharmony_ci} 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_cistatic void 303262306a36Sopenharmony_cimlxsw_sp_fdb_call_notifiers(enum switchdev_notifier_type type, 303362306a36Sopenharmony_ci const char *mac, u16 vid, 303462306a36Sopenharmony_ci struct net_device *dev, bool offloaded, bool locked) 303562306a36Sopenharmony_ci{ 303662306a36Sopenharmony_ci struct switchdev_notifier_fdb_info info = {}; 303762306a36Sopenharmony_ci 303862306a36Sopenharmony_ci info.addr = mac; 303962306a36Sopenharmony_ci info.vid = vid; 304062306a36Sopenharmony_ci info.offloaded = offloaded; 304162306a36Sopenharmony_ci info.locked = locked; 304262306a36Sopenharmony_ci call_switchdev_notifiers(type, dev, &info.info, NULL); 304362306a36Sopenharmony_ci} 304462306a36Sopenharmony_ci 304562306a36Sopenharmony_cistatic void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, 304662306a36Sopenharmony_ci char *sfn_pl, int rec_index, 304762306a36Sopenharmony_ci bool adding) 304862306a36Sopenharmony_ci{ 304962306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 305062306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 305162306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 305262306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 305362306a36Sopenharmony_ci u16 local_port, vid, fid, evid = 0; 305462306a36Sopenharmony_ci enum switchdev_notifier_type type; 305562306a36Sopenharmony_ci char mac[ETH_ALEN]; 305662306a36Sopenharmony_ci bool do_notification = true; 305762306a36Sopenharmony_ci int err; 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_ci mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &fid, &local_port); 306062306a36Sopenharmony_ci 306162306a36Sopenharmony_ci if (WARN_ON_ONCE(!mlxsw_sp_local_port_is_valid(mlxsw_sp, local_port))) 306262306a36Sopenharmony_ci return; 306362306a36Sopenharmony_ci mlxsw_sp_port = mlxsw_sp->ports[local_port]; 306462306a36Sopenharmony_ci if (!mlxsw_sp_port) { 306562306a36Sopenharmony_ci dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect local port in FDB notification\n"); 306662306a36Sopenharmony_ci goto just_remove; 306762306a36Sopenharmony_ci } 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_fid(mlxsw_sp_port, fid); 307062306a36Sopenharmony_ci if (!mlxsw_sp_port_vlan) { 307162306a36Sopenharmony_ci netdev_err(mlxsw_sp_port->dev, "Failed to find a matching {Port, VID} following FDB notification\n"); 307262306a36Sopenharmony_ci goto just_remove; 307362306a36Sopenharmony_ci } 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci bridge_port = mlxsw_sp_port_vlan->bridge_port; 307662306a36Sopenharmony_ci if (!bridge_port) { 307762306a36Sopenharmony_ci netdev_err(mlxsw_sp_port->dev, "{Port, VID} not associated with a bridge\n"); 307862306a36Sopenharmony_ci goto just_remove; 307962306a36Sopenharmony_ci } 308062306a36Sopenharmony_ci 308162306a36Sopenharmony_ci bridge_device = bridge_port->bridge_device; 308262306a36Sopenharmony_ci vid = bridge_device->vlan_enabled ? mlxsw_sp_port_vlan->vid : 0; 308362306a36Sopenharmony_ci evid = mlxsw_sp_port_vlan->vid; 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_ci if (adding && mlxsw_sp_port->security) { 308662306a36Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, mac, 308762306a36Sopenharmony_ci vid, bridge_port->dev, false, true); 308862306a36Sopenharmony_ci return; 308962306a36Sopenharmony_ci } 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_cido_fdb_op: 309262306a36Sopenharmony_ci err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid, evid, 309362306a36Sopenharmony_ci adding, true); 309462306a36Sopenharmony_ci if (err) { 309562306a36Sopenharmony_ci dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to set FDB entry\n"); 309662306a36Sopenharmony_ci return; 309762306a36Sopenharmony_ci } 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_ci if (!do_notification) 310062306a36Sopenharmony_ci return; 310162306a36Sopenharmony_ci type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; 310262306a36Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev, adding, 310362306a36Sopenharmony_ci false); 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_ci return; 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_cijust_remove: 310862306a36Sopenharmony_ci adding = false; 310962306a36Sopenharmony_ci do_notification = false; 311062306a36Sopenharmony_ci goto do_fdb_op; 311162306a36Sopenharmony_ci} 311262306a36Sopenharmony_ci 311362306a36Sopenharmony_cistatic void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, 311462306a36Sopenharmony_ci char *sfn_pl, int rec_index, 311562306a36Sopenharmony_ci bool adding) 311662306a36Sopenharmony_ci{ 311762306a36Sopenharmony_ci struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; 311862306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 311962306a36Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 312062306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 312162306a36Sopenharmony_ci enum switchdev_notifier_type type; 312262306a36Sopenharmony_ci char mac[ETH_ALEN]; 312362306a36Sopenharmony_ci u16 lag_vid = 0; 312462306a36Sopenharmony_ci u16 lag_id; 312562306a36Sopenharmony_ci u16 vid, fid; 312662306a36Sopenharmony_ci bool do_notification = true; 312762306a36Sopenharmony_ci int err; 312862306a36Sopenharmony_ci 312962306a36Sopenharmony_ci mlxsw_reg_sfn_mac_lag_unpack(sfn_pl, rec_index, mac, &fid, &lag_id); 313062306a36Sopenharmony_ci mlxsw_sp_port = mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id); 313162306a36Sopenharmony_ci if (!mlxsw_sp_port) { 313262306a36Sopenharmony_ci dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Cannot find port representor for LAG\n"); 313362306a36Sopenharmony_ci goto just_remove; 313462306a36Sopenharmony_ci } 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_fid(mlxsw_sp_port, fid); 313762306a36Sopenharmony_ci if (!mlxsw_sp_port_vlan) { 313862306a36Sopenharmony_ci netdev_err(mlxsw_sp_port->dev, "Failed to find a matching {Port, VID} following FDB notification\n"); 313962306a36Sopenharmony_ci goto just_remove; 314062306a36Sopenharmony_ci } 314162306a36Sopenharmony_ci 314262306a36Sopenharmony_ci bridge_port = mlxsw_sp_port_vlan->bridge_port; 314362306a36Sopenharmony_ci if (!bridge_port) { 314462306a36Sopenharmony_ci netdev_err(mlxsw_sp_port->dev, "{Port, VID} not associated with a bridge\n"); 314562306a36Sopenharmony_ci goto just_remove; 314662306a36Sopenharmony_ci } 314762306a36Sopenharmony_ci 314862306a36Sopenharmony_ci bridge_device = bridge_port->bridge_device; 314962306a36Sopenharmony_ci vid = bridge_device->vlan_enabled ? mlxsw_sp_port_vlan->vid : 0; 315062306a36Sopenharmony_ci lag_vid = mlxsw_sp_port_vlan->vid; 315162306a36Sopenharmony_ci 315262306a36Sopenharmony_ci if (adding && mlxsw_sp_port->security) { 315362306a36Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, mac, 315462306a36Sopenharmony_ci vid, bridge_port->dev, false, true); 315562306a36Sopenharmony_ci return; 315662306a36Sopenharmony_ci } 315762306a36Sopenharmony_ci 315862306a36Sopenharmony_cido_fdb_op: 315962306a36Sopenharmony_ci err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid, 316062306a36Sopenharmony_ci adding, true); 316162306a36Sopenharmony_ci if (err) { 316262306a36Sopenharmony_ci dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to set FDB entry\n"); 316362306a36Sopenharmony_ci return; 316462306a36Sopenharmony_ci } 316562306a36Sopenharmony_ci 316662306a36Sopenharmony_ci if (!do_notification) 316762306a36Sopenharmony_ci return; 316862306a36Sopenharmony_ci type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; 316962306a36Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev, adding, 317062306a36Sopenharmony_ci false); 317162306a36Sopenharmony_ci 317262306a36Sopenharmony_ci return; 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_cijust_remove: 317562306a36Sopenharmony_ci adding = false; 317662306a36Sopenharmony_ci do_notification = false; 317762306a36Sopenharmony_ci goto do_fdb_op; 317862306a36Sopenharmony_ci} 317962306a36Sopenharmony_ci 318062306a36Sopenharmony_cistatic int 318162306a36Sopenharmony_ci__mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp, 318262306a36Sopenharmony_ci const struct mlxsw_sp_fid *fid, 318362306a36Sopenharmony_ci bool adding, 318462306a36Sopenharmony_ci struct net_device **nve_dev, 318562306a36Sopenharmony_ci u16 *p_vid, __be32 *p_vni) 318662306a36Sopenharmony_ci{ 318762306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 318862306a36Sopenharmony_ci struct net_device *br_dev, *dev; 318962306a36Sopenharmony_ci int nve_ifindex; 319062306a36Sopenharmony_ci int err; 319162306a36Sopenharmony_ci 319262306a36Sopenharmony_ci err = mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex); 319362306a36Sopenharmony_ci if (err) 319462306a36Sopenharmony_ci return err; 319562306a36Sopenharmony_ci 319662306a36Sopenharmony_ci err = mlxsw_sp_fid_vni(fid, p_vni); 319762306a36Sopenharmony_ci if (err) 319862306a36Sopenharmony_ci return err; 319962306a36Sopenharmony_ci 320062306a36Sopenharmony_ci dev = __dev_get_by_index(mlxsw_sp_net(mlxsw_sp), nve_ifindex); 320162306a36Sopenharmony_ci if (!dev) 320262306a36Sopenharmony_ci return -EINVAL; 320362306a36Sopenharmony_ci *nve_dev = dev; 320462306a36Sopenharmony_ci 320562306a36Sopenharmony_ci if (!netif_running(dev)) 320662306a36Sopenharmony_ci return -EINVAL; 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_ci if (adding && !br_port_flag_is_set(dev, BR_LEARNING)) 320962306a36Sopenharmony_ci return -EINVAL; 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_ci if (adding && netif_is_vxlan(dev)) { 321262306a36Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(dev); 321362306a36Sopenharmony_ci 321462306a36Sopenharmony_ci if (!(vxlan->cfg.flags & VXLAN_F_LEARN)) 321562306a36Sopenharmony_ci return -EINVAL; 321662306a36Sopenharmony_ci } 321762306a36Sopenharmony_ci 321862306a36Sopenharmony_ci br_dev = netdev_master_upper_dev_get(dev); 321962306a36Sopenharmony_ci if (!br_dev) 322062306a36Sopenharmony_ci return -EINVAL; 322162306a36Sopenharmony_ci 322262306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 322362306a36Sopenharmony_ci if (!bridge_device) 322462306a36Sopenharmony_ci return -EINVAL; 322562306a36Sopenharmony_ci 322662306a36Sopenharmony_ci *p_vid = bridge_device->ops->fid_vid(bridge_device, fid); 322762306a36Sopenharmony_ci 322862306a36Sopenharmony_ci return 0; 322962306a36Sopenharmony_ci} 323062306a36Sopenharmony_ci 323162306a36Sopenharmony_cistatic void mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp, 323262306a36Sopenharmony_ci char *sfn_pl, 323362306a36Sopenharmony_ci int rec_index, 323462306a36Sopenharmony_ci bool adding) 323562306a36Sopenharmony_ci{ 323662306a36Sopenharmony_ci enum mlxsw_reg_sfn_uc_tunnel_protocol sfn_proto; 323762306a36Sopenharmony_ci enum switchdev_notifier_type type; 323862306a36Sopenharmony_ci struct net_device *nve_dev; 323962306a36Sopenharmony_ci union mlxsw_sp_l3addr addr; 324062306a36Sopenharmony_ci struct mlxsw_sp_fid *fid; 324162306a36Sopenharmony_ci char mac[ETH_ALEN]; 324262306a36Sopenharmony_ci u16 fid_index, vid; 324362306a36Sopenharmony_ci __be32 vni; 324462306a36Sopenharmony_ci u32 uip; 324562306a36Sopenharmony_ci int err; 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci mlxsw_reg_sfn_uc_tunnel_unpack(sfn_pl, rec_index, mac, &fid_index, 324862306a36Sopenharmony_ci &uip, &sfn_proto); 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_ci fid = mlxsw_sp_fid_lookup_by_index(mlxsw_sp, fid_index); 325162306a36Sopenharmony_ci if (!fid) 325262306a36Sopenharmony_ci goto err_fid_lookup; 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ci err = mlxsw_sp_nve_learned_ip_resolve(mlxsw_sp, uip, 325562306a36Sopenharmony_ci (enum mlxsw_sp_l3proto) sfn_proto, 325662306a36Sopenharmony_ci &addr); 325762306a36Sopenharmony_ci if (err) 325862306a36Sopenharmony_ci goto err_ip_resolve; 325962306a36Sopenharmony_ci 326062306a36Sopenharmony_ci err = __mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, fid, adding, 326162306a36Sopenharmony_ci &nve_dev, &vid, &vni); 326262306a36Sopenharmony_ci if (err) 326362306a36Sopenharmony_ci goto err_fdb_process; 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_ci err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, mac, fid_index, 326662306a36Sopenharmony_ci (enum mlxsw_sp_l3proto) sfn_proto, 326762306a36Sopenharmony_ci &addr, adding, true); 326862306a36Sopenharmony_ci if (err) 326962306a36Sopenharmony_ci goto err_fdb_op; 327062306a36Sopenharmony_ci 327162306a36Sopenharmony_ci mlxsw_sp_fdb_nve_call_notifiers(nve_dev, mac, 327262306a36Sopenharmony_ci (enum mlxsw_sp_l3proto) sfn_proto, 327362306a36Sopenharmony_ci &addr, vni, adding); 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : 327662306a36Sopenharmony_ci SWITCHDEV_FDB_DEL_TO_BRIDGE; 327762306a36Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(type, mac, vid, nve_dev, adding, false); 327862306a36Sopenharmony_ci 327962306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_ci return; 328262306a36Sopenharmony_ci 328362306a36Sopenharmony_cierr_fdb_op: 328462306a36Sopenharmony_cierr_fdb_process: 328562306a36Sopenharmony_cierr_ip_resolve: 328662306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 328762306a36Sopenharmony_cierr_fid_lookup: 328862306a36Sopenharmony_ci /* Remove an FDB entry in case we cannot process it. Otherwise the 328962306a36Sopenharmony_ci * device will keep sending the same notification over and over again. 329062306a36Sopenharmony_ci */ 329162306a36Sopenharmony_ci mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, mac, fid_index, 329262306a36Sopenharmony_ci (enum mlxsw_sp_l3proto) sfn_proto, &addr, 329362306a36Sopenharmony_ci false, true); 329462306a36Sopenharmony_ci} 329562306a36Sopenharmony_ci 329662306a36Sopenharmony_cistatic void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp, 329762306a36Sopenharmony_ci char *sfn_pl, int rec_index) 329862306a36Sopenharmony_ci{ 329962306a36Sopenharmony_ci switch (mlxsw_reg_sfn_rec_type_get(sfn_pl, rec_index)) { 330062306a36Sopenharmony_ci case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC: 330162306a36Sopenharmony_ci mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl, 330262306a36Sopenharmony_ci rec_index, true); 330362306a36Sopenharmony_ci break; 330462306a36Sopenharmony_ci case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC: 330562306a36Sopenharmony_ci mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl, 330662306a36Sopenharmony_ci rec_index, false); 330762306a36Sopenharmony_ci break; 330862306a36Sopenharmony_ci case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC_LAG: 330962306a36Sopenharmony_ci mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl, 331062306a36Sopenharmony_ci rec_index, true); 331162306a36Sopenharmony_ci break; 331262306a36Sopenharmony_ci case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC_LAG: 331362306a36Sopenharmony_ci mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl, 331462306a36Sopenharmony_ci rec_index, false); 331562306a36Sopenharmony_ci break; 331662306a36Sopenharmony_ci case MLXSW_REG_SFN_REC_TYPE_LEARNED_UNICAST_TUNNEL: 331762306a36Sopenharmony_ci mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, sfn_pl, 331862306a36Sopenharmony_ci rec_index, true); 331962306a36Sopenharmony_ci break; 332062306a36Sopenharmony_ci case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_UNICAST_TUNNEL: 332162306a36Sopenharmony_ci mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, sfn_pl, 332262306a36Sopenharmony_ci rec_index, false); 332362306a36Sopenharmony_ci break; 332462306a36Sopenharmony_ci } 332562306a36Sopenharmony_ci} 332662306a36Sopenharmony_ci 332762306a36Sopenharmony_ci#define MLXSW_SP_FDB_SFN_QUERIES_PER_SESSION 10 332862306a36Sopenharmony_ci 332962306a36Sopenharmony_cistatic void mlxsw_sp_fdb_notify_work(struct work_struct *work) 333062306a36Sopenharmony_ci{ 333162306a36Sopenharmony_ci struct mlxsw_sp_bridge *bridge; 333262306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 333362306a36Sopenharmony_ci bool reschedule = false; 333462306a36Sopenharmony_ci char *sfn_pl; 333562306a36Sopenharmony_ci int queries; 333662306a36Sopenharmony_ci u8 num_rec; 333762306a36Sopenharmony_ci int i; 333862306a36Sopenharmony_ci int err; 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_ci sfn_pl = kmalloc(MLXSW_REG_SFN_LEN, GFP_KERNEL); 334162306a36Sopenharmony_ci if (!sfn_pl) 334262306a36Sopenharmony_ci return; 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_ci bridge = container_of(work, struct mlxsw_sp_bridge, fdb_notify.dw.work); 334562306a36Sopenharmony_ci mlxsw_sp = bridge->mlxsw_sp; 334662306a36Sopenharmony_ci 334762306a36Sopenharmony_ci rtnl_lock(); 334862306a36Sopenharmony_ci if (list_empty(&bridge->bridges_list)) 334962306a36Sopenharmony_ci goto out; 335062306a36Sopenharmony_ci reschedule = true; 335162306a36Sopenharmony_ci queries = MLXSW_SP_FDB_SFN_QUERIES_PER_SESSION; 335262306a36Sopenharmony_ci while (queries > 0) { 335362306a36Sopenharmony_ci mlxsw_reg_sfn_pack(sfn_pl); 335462306a36Sopenharmony_ci err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl); 335562306a36Sopenharmony_ci if (err) { 335662306a36Sopenharmony_ci dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get FDB notifications\n"); 335762306a36Sopenharmony_ci goto out; 335862306a36Sopenharmony_ci } 335962306a36Sopenharmony_ci num_rec = mlxsw_reg_sfn_num_rec_get(sfn_pl); 336062306a36Sopenharmony_ci for (i = 0; i < num_rec; i++) 336162306a36Sopenharmony_ci mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i); 336262306a36Sopenharmony_ci if (num_rec != MLXSW_REG_SFN_REC_MAX_COUNT) 336362306a36Sopenharmony_ci goto out; 336462306a36Sopenharmony_ci queries--; 336562306a36Sopenharmony_ci } 336662306a36Sopenharmony_ci 336762306a36Sopenharmony_ciout: 336862306a36Sopenharmony_ci rtnl_unlock(); 336962306a36Sopenharmony_ci kfree(sfn_pl); 337062306a36Sopenharmony_ci if (!reschedule) 337162306a36Sopenharmony_ci return; 337262306a36Sopenharmony_ci mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp, !queries); 337362306a36Sopenharmony_ci} 337462306a36Sopenharmony_ci 337562306a36Sopenharmony_cistruct mlxsw_sp_switchdev_event_work { 337662306a36Sopenharmony_ci struct work_struct work; 337762306a36Sopenharmony_ci netdevice_tracker dev_tracker; 337862306a36Sopenharmony_ci union { 337962306a36Sopenharmony_ci struct switchdev_notifier_fdb_info fdb_info; 338062306a36Sopenharmony_ci struct switchdev_notifier_vxlan_fdb_info vxlan_fdb_info; 338162306a36Sopenharmony_ci }; 338262306a36Sopenharmony_ci struct net_device *dev; 338362306a36Sopenharmony_ci unsigned long event; 338462306a36Sopenharmony_ci}; 338562306a36Sopenharmony_ci 338662306a36Sopenharmony_cistatic void 338762306a36Sopenharmony_cimlxsw_sp_switchdev_bridge_vxlan_fdb_event(struct mlxsw_sp *mlxsw_sp, 338862306a36Sopenharmony_ci struct mlxsw_sp_switchdev_event_work * 338962306a36Sopenharmony_ci switchdev_work, 339062306a36Sopenharmony_ci struct mlxsw_sp_fid *fid, __be32 vni) 339162306a36Sopenharmony_ci{ 339262306a36Sopenharmony_ci struct switchdev_notifier_vxlan_fdb_info vxlan_fdb_info; 339362306a36Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info; 339462306a36Sopenharmony_ci struct net_device *dev = switchdev_work->dev; 339562306a36Sopenharmony_ci enum mlxsw_sp_l3proto proto; 339662306a36Sopenharmony_ci union mlxsw_sp_l3addr addr; 339762306a36Sopenharmony_ci int err; 339862306a36Sopenharmony_ci 339962306a36Sopenharmony_ci fdb_info = &switchdev_work->fdb_info; 340062306a36Sopenharmony_ci err = vxlan_fdb_find_uc(dev, fdb_info->addr, vni, &vxlan_fdb_info); 340162306a36Sopenharmony_ci if (err) 340262306a36Sopenharmony_ci return; 340362306a36Sopenharmony_ci 340462306a36Sopenharmony_ci mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info.remote_ip, 340562306a36Sopenharmony_ci &proto, &addr); 340662306a36Sopenharmony_ci 340762306a36Sopenharmony_ci switch (switchdev_work->event) { 340862306a36Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 340962306a36Sopenharmony_ci err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, 341062306a36Sopenharmony_ci vxlan_fdb_info.eth_addr, 341162306a36Sopenharmony_ci mlxsw_sp_fid_index(fid), 341262306a36Sopenharmony_ci proto, &addr, true, false); 341362306a36Sopenharmony_ci if (err) 341462306a36Sopenharmony_ci return; 341562306a36Sopenharmony_ci vxlan_fdb_info.offloaded = true; 341662306a36Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, 341762306a36Sopenharmony_ci &vxlan_fdb_info.info, NULL); 341862306a36Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, 341962306a36Sopenharmony_ci vxlan_fdb_info.eth_addr, 342062306a36Sopenharmony_ci fdb_info->vid, dev, true, false); 342162306a36Sopenharmony_ci break; 342262306a36Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 342362306a36Sopenharmony_ci err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, 342462306a36Sopenharmony_ci vxlan_fdb_info.eth_addr, 342562306a36Sopenharmony_ci mlxsw_sp_fid_index(fid), 342662306a36Sopenharmony_ci proto, &addr, false, 342762306a36Sopenharmony_ci false); 342862306a36Sopenharmony_ci vxlan_fdb_info.offloaded = false; 342962306a36Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, 343062306a36Sopenharmony_ci &vxlan_fdb_info.info, NULL); 343162306a36Sopenharmony_ci break; 343262306a36Sopenharmony_ci } 343362306a36Sopenharmony_ci} 343462306a36Sopenharmony_ci 343562306a36Sopenharmony_cistatic void 343662306a36Sopenharmony_cimlxsw_sp_switchdev_bridge_nve_fdb_event(struct mlxsw_sp_switchdev_event_work * 343762306a36Sopenharmony_ci switchdev_work) 343862306a36Sopenharmony_ci{ 343962306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 344062306a36Sopenharmony_ci struct net_device *dev = switchdev_work->dev; 344162306a36Sopenharmony_ci struct net_device *br_dev; 344262306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 344362306a36Sopenharmony_ci struct mlxsw_sp_fid *fid; 344462306a36Sopenharmony_ci __be32 vni; 344562306a36Sopenharmony_ci int err; 344662306a36Sopenharmony_ci 344762306a36Sopenharmony_ci if (switchdev_work->event != SWITCHDEV_FDB_ADD_TO_DEVICE && 344862306a36Sopenharmony_ci switchdev_work->event != SWITCHDEV_FDB_DEL_TO_DEVICE) 344962306a36Sopenharmony_ci return; 345062306a36Sopenharmony_ci 345162306a36Sopenharmony_ci if (switchdev_work->event == SWITCHDEV_FDB_ADD_TO_DEVICE && 345262306a36Sopenharmony_ci (!switchdev_work->fdb_info.added_by_user || 345362306a36Sopenharmony_ci switchdev_work->fdb_info.is_local)) 345462306a36Sopenharmony_ci return; 345562306a36Sopenharmony_ci 345662306a36Sopenharmony_ci if (!netif_running(dev)) 345762306a36Sopenharmony_ci return; 345862306a36Sopenharmony_ci br_dev = netdev_master_upper_dev_get(dev); 345962306a36Sopenharmony_ci if (!br_dev) 346062306a36Sopenharmony_ci return; 346162306a36Sopenharmony_ci if (!netif_is_bridge_master(br_dev)) 346262306a36Sopenharmony_ci return; 346362306a36Sopenharmony_ci mlxsw_sp = mlxsw_sp_lower_get(br_dev); 346462306a36Sopenharmony_ci if (!mlxsw_sp) 346562306a36Sopenharmony_ci return; 346662306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 346762306a36Sopenharmony_ci if (!bridge_device) 346862306a36Sopenharmony_ci return; 346962306a36Sopenharmony_ci 347062306a36Sopenharmony_ci fid = bridge_device->ops->fid_lookup(bridge_device, 347162306a36Sopenharmony_ci switchdev_work->fdb_info.vid); 347262306a36Sopenharmony_ci if (!fid) 347362306a36Sopenharmony_ci return; 347462306a36Sopenharmony_ci 347562306a36Sopenharmony_ci err = mlxsw_sp_fid_vni(fid, &vni); 347662306a36Sopenharmony_ci if (err) 347762306a36Sopenharmony_ci goto out; 347862306a36Sopenharmony_ci 347962306a36Sopenharmony_ci mlxsw_sp_switchdev_bridge_vxlan_fdb_event(mlxsw_sp, switchdev_work, fid, 348062306a36Sopenharmony_ci vni); 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_ciout: 348362306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 348462306a36Sopenharmony_ci} 348562306a36Sopenharmony_ci 348662306a36Sopenharmony_cistatic void mlxsw_sp_switchdev_bridge_fdb_event_work(struct work_struct *work) 348762306a36Sopenharmony_ci{ 348862306a36Sopenharmony_ci struct mlxsw_sp_switchdev_event_work *switchdev_work = 348962306a36Sopenharmony_ci container_of(work, struct mlxsw_sp_switchdev_event_work, work); 349062306a36Sopenharmony_ci struct net_device *dev = switchdev_work->dev; 349162306a36Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info; 349262306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 349362306a36Sopenharmony_ci int err; 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_ci rtnl_lock(); 349662306a36Sopenharmony_ci if (netif_is_vxlan(dev)) { 349762306a36Sopenharmony_ci mlxsw_sp_switchdev_bridge_nve_fdb_event(switchdev_work); 349862306a36Sopenharmony_ci goto out; 349962306a36Sopenharmony_ci } 350062306a36Sopenharmony_ci 350162306a36Sopenharmony_ci mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(dev); 350262306a36Sopenharmony_ci if (!mlxsw_sp_port) 350362306a36Sopenharmony_ci goto out; 350462306a36Sopenharmony_ci 350562306a36Sopenharmony_ci switch (switchdev_work->event) { 350662306a36Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 350762306a36Sopenharmony_ci fdb_info = &switchdev_work->fdb_info; 350862306a36Sopenharmony_ci if (!fdb_info->added_by_user || fdb_info->is_local) 350962306a36Sopenharmony_ci break; 351062306a36Sopenharmony_ci err = mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, true); 351162306a36Sopenharmony_ci if (err) 351262306a36Sopenharmony_ci break; 351362306a36Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, 351462306a36Sopenharmony_ci fdb_info->addr, 351562306a36Sopenharmony_ci fdb_info->vid, dev, true, false); 351662306a36Sopenharmony_ci break; 351762306a36Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 351862306a36Sopenharmony_ci fdb_info = &switchdev_work->fdb_info; 351962306a36Sopenharmony_ci mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, false); 352062306a36Sopenharmony_ci break; 352162306a36Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_BRIDGE: 352262306a36Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_BRIDGE: 352362306a36Sopenharmony_ci /* These events are only used to potentially update an existing 352462306a36Sopenharmony_ci * SPAN mirror. 352562306a36Sopenharmony_ci */ 352662306a36Sopenharmony_ci break; 352762306a36Sopenharmony_ci } 352862306a36Sopenharmony_ci 352962306a36Sopenharmony_ci mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp); 353062306a36Sopenharmony_ci 353162306a36Sopenharmony_ciout: 353262306a36Sopenharmony_ci rtnl_unlock(); 353362306a36Sopenharmony_ci kfree(switchdev_work->fdb_info.addr); 353462306a36Sopenharmony_ci netdev_put(dev, &switchdev_work->dev_tracker); 353562306a36Sopenharmony_ci kfree(switchdev_work); 353662306a36Sopenharmony_ci} 353762306a36Sopenharmony_ci 353862306a36Sopenharmony_cistatic void 353962306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_fdb_add(struct mlxsw_sp *mlxsw_sp, 354062306a36Sopenharmony_ci struct mlxsw_sp_switchdev_event_work * 354162306a36Sopenharmony_ci switchdev_work) 354262306a36Sopenharmony_ci{ 354362306a36Sopenharmony_ci struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info; 354462306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 354562306a36Sopenharmony_ci struct net_device *dev = switchdev_work->dev; 354662306a36Sopenharmony_ci enum mlxsw_sp_l3proto proto; 354762306a36Sopenharmony_ci union mlxsw_sp_l3addr addr; 354862306a36Sopenharmony_ci struct net_device *br_dev; 354962306a36Sopenharmony_ci struct mlxsw_sp_fid *fid; 355062306a36Sopenharmony_ci u16 vid; 355162306a36Sopenharmony_ci int err; 355262306a36Sopenharmony_ci 355362306a36Sopenharmony_ci vxlan_fdb_info = &switchdev_work->vxlan_fdb_info; 355462306a36Sopenharmony_ci br_dev = netdev_master_upper_dev_get(dev); 355562306a36Sopenharmony_ci 355662306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 355762306a36Sopenharmony_ci if (!bridge_device) 355862306a36Sopenharmony_ci return; 355962306a36Sopenharmony_ci 356062306a36Sopenharmony_ci fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan_fdb_info->vni); 356162306a36Sopenharmony_ci if (!fid) 356262306a36Sopenharmony_ci return; 356362306a36Sopenharmony_ci 356462306a36Sopenharmony_ci mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info->remote_ip, 356562306a36Sopenharmony_ci &proto, &addr); 356662306a36Sopenharmony_ci 356762306a36Sopenharmony_ci if (is_zero_ether_addr(vxlan_fdb_info->eth_addr)) { 356862306a36Sopenharmony_ci err = mlxsw_sp_nve_flood_ip_add(mlxsw_sp, fid, proto, &addr); 356962306a36Sopenharmony_ci if (err) { 357062306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 357162306a36Sopenharmony_ci return; 357262306a36Sopenharmony_ci } 357362306a36Sopenharmony_ci vxlan_fdb_info->offloaded = true; 357462306a36Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, 357562306a36Sopenharmony_ci &vxlan_fdb_info->info, NULL); 357662306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 357762306a36Sopenharmony_ci return; 357862306a36Sopenharmony_ci } 357962306a36Sopenharmony_ci 358062306a36Sopenharmony_ci /* The device has a single FDB table, whereas Linux has two - one 358162306a36Sopenharmony_ci * in the bridge driver and another in the VxLAN driver. We only 358262306a36Sopenharmony_ci * program an entry to the device if the MAC points to the VxLAN 358362306a36Sopenharmony_ci * device in the bridge's FDB table 358462306a36Sopenharmony_ci */ 358562306a36Sopenharmony_ci vid = bridge_device->ops->fid_vid(bridge_device, fid); 358662306a36Sopenharmony_ci if (br_fdb_find_port(br_dev, vxlan_fdb_info->eth_addr, vid) != dev) 358762306a36Sopenharmony_ci goto err_br_fdb_find; 358862306a36Sopenharmony_ci 358962306a36Sopenharmony_ci err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, vxlan_fdb_info->eth_addr, 359062306a36Sopenharmony_ci mlxsw_sp_fid_index(fid), proto, 359162306a36Sopenharmony_ci &addr, true, false); 359262306a36Sopenharmony_ci if (err) 359362306a36Sopenharmony_ci goto err_fdb_tunnel_uc_op; 359462306a36Sopenharmony_ci vxlan_fdb_info->offloaded = true; 359562306a36Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, 359662306a36Sopenharmony_ci &vxlan_fdb_info->info, NULL); 359762306a36Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, 359862306a36Sopenharmony_ci vxlan_fdb_info->eth_addr, vid, dev, true, 359962306a36Sopenharmony_ci false); 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 360262306a36Sopenharmony_ci 360362306a36Sopenharmony_ci return; 360462306a36Sopenharmony_ci 360562306a36Sopenharmony_cierr_fdb_tunnel_uc_op: 360662306a36Sopenharmony_cierr_br_fdb_find: 360762306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 360862306a36Sopenharmony_ci} 360962306a36Sopenharmony_ci 361062306a36Sopenharmony_cistatic void 361162306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_fdb_del(struct mlxsw_sp *mlxsw_sp, 361262306a36Sopenharmony_ci struct mlxsw_sp_switchdev_event_work * 361362306a36Sopenharmony_ci switchdev_work) 361462306a36Sopenharmony_ci{ 361562306a36Sopenharmony_ci struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info; 361662306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 361762306a36Sopenharmony_ci struct net_device *dev = switchdev_work->dev; 361862306a36Sopenharmony_ci struct net_device *br_dev = netdev_master_upper_dev_get(dev); 361962306a36Sopenharmony_ci enum mlxsw_sp_l3proto proto; 362062306a36Sopenharmony_ci union mlxsw_sp_l3addr addr; 362162306a36Sopenharmony_ci struct mlxsw_sp_fid *fid; 362262306a36Sopenharmony_ci u16 vid; 362362306a36Sopenharmony_ci 362462306a36Sopenharmony_ci vxlan_fdb_info = &switchdev_work->vxlan_fdb_info; 362562306a36Sopenharmony_ci if (!vxlan_fdb_info->offloaded) 362662306a36Sopenharmony_ci return; 362762306a36Sopenharmony_ci 362862306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 362962306a36Sopenharmony_ci if (!bridge_device) 363062306a36Sopenharmony_ci return; 363162306a36Sopenharmony_ci 363262306a36Sopenharmony_ci fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan_fdb_info->vni); 363362306a36Sopenharmony_ci if (!fid) 363462306a36Sopenharmony_ci return; 363562306a36Sopenharmony_ci 363662306a36Sopenharmony_ci mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info->remote_ip, 363762306a36Sopenharmony_ci &proto, &addr); 363862306a36Sopenharmony_ci 363962306a36Sopenharmony_ci if (is_zero_ether_addr(vxlan_fdb_info->eth_addr)) { 364062306a36Sopenharmony_ci mlxsw_sp_nve_flood_ip_del(mlxsw_sp, fid, proto, &addr); 364162306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 364262306a36Sopenharmony_ci return; 364362306a36Sopenharmony_ci } 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_ci mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, vxlan_fdb_info->eth_addr, 364662306a36Sopenharmony_ci mlxsw_sp_fid_index(fid), proto, &addr, 364762306a36Sopenharmony_ci false, false); 364862306a36Sopenharmony_ci vid = bridge_device->ops->fid_vid(bridge_device, fid); 364962306a36Sopenharmony_ci mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, 365062306a36Sopenharmony_ci vxlan_fdb_info->eth_addr, vid, dev, false, 365162306a36Sopenharmony_ci false); 365262306a36Sopenharmony_ci 365362306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 365462306a36Sopenharmony_ci} 365562306a36Sopenharmony_ci 365662306a36Sopenharmony_cistatic void mlxsw_sp_switchdev_vxlan_fdb_event_work(struct work_struct *work) 365762306a36Sopenharmony_ci{ 365862306a36Sopenharmony_ci struct mlxsw_sp_switchdev_event_work *switchdev_work = 365962306a36Sopenharmony_ci container_of(work, struct mlxsw_sp_switchdev_event_work, work); 366062306a36Sopenharmony_ci struct net_device *dev = switchdev_work->dev; 366162306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 366262306a36Sopenharmony_ci struct net_device *br_dev; 366362306a36Sopenharmony_ci 366462306a36Sopenharmony_ci rtnl_lock(); 366562306a36Sopenharmony_ci 366662306a36Sopenharmony_ci if (!netif_running(dev)) 366762306a36Sopenharmony_ci goto out; 366862306a36Sopenharmony_ci br_dev = netdev_master_upper_dev_get(dev); 366962306a36Sopenharmony_ci if (!br_dev) 367062306a36Sopenharmony_ci goto out; 367162306a36Sopenharmony_ci if (!netif_is_bridge_master(br_dev)) 367262306a36Sopenharmony_ci goto out; 367362306a36Sopenharmony_ci mlxsw_sp = mlxsw_sp_lower_get(br_dev); 367462306a36Sopenharmony_ci if (!mlxsw_sp) 367562306a36Sopenharmony_ci goto out; 367662306a36Sopenharmony_ci 367762306a36Sopenharmony_ci switch (switchdev_work->event) { 367862306a36Sopenharmony_ci case SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE: 367962306a36Sopenharmony_ci mlxsw_sp_switchdev_vxlan_fdb_add(mlxsw_sp, switchdev_work); 368062306a36Sopenharmony_ci break; 368162306a36Sopenharmony_ci case SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE: 368262306a36Sopenharmony_ci mlxsw_sp_switchdev_vxlan_fdb_del(mlxsw_sp, switchdev_work); 368362306a36Sopenharmony_ci break; 368462306a36Sopenharmony_ci } 368562306a36Sopenharmony_ci 368662306a36Sopenharmony_ciout: 368762306a36Sopenharmony_ci rtnl_unlock(); 368862306a36Sopenharmony_ci netdev_put(dev, &switchdev_work->dev_tracker); 368962306a36Sopenharmony_ci kfree(switchdev_work); 369062306a36Sopenharmony_ci} 369162306a36Sopenharmony_ci 369262306a36Sopenharmony_cistatic int 369362306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_work_prepare(struct mlxsw_sp_switchdev_event_work * 369462306a36Sopenharmony_ci switchdev_work, 369562306a36Sopenharmony_ci struct switchdev_notifier_info *info) 369662306a36Sopenharmony_ci{ 369762306a36Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(switchdev_work->dev); 369862306a36Sopenharmony_ci struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info; 369962306a36Sopenharmony_ci struct vxlan_config *cfg = &vxlan->cfg; 370062306a36Sopenharmony_ci struct netlink_ext_ack *extack; 370162306a36Sopenharmony_ci 370262306a36Sopenharmony_ci extack = switchdev_notifier_info_to_extack(info); 370362306a36Sopenharmony_ci vxlan_fdb_info = container_of(info, 370462306a36Sopenharmony_ci struct switchdev_notifier_vxlan_fdb_info, 370562306a36Sopenharmony_ci info); 370662306a36Sopenharmony_ci 370762306a36Sopenharmony_ci if (vxlan_fdb_info->remote_port != cfg->dst_port) { 370862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Non-default remote port is not supported"); 370962306a36Sopenharmony_ci return -EOPNOTSUPP; 371062306a36Sopenharmony_ci } 371162306a36Sopenharmony_ci if (vxlan_fdb_info->remote_vni != cfg->vni || 371262306a36Sopenharmony_ci vxlan_fdb_info->vni != cfg->vni) { 371362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Non-default VNI is not supported"); 371462306a36Sopenharmony_ci return -EOPNOTSUPP; 371562306a36Sopenharmony_ci } 371662306a36Sopenharmony_ci if (vxlan_fdb_info->remote_ifindex) { 371762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Local interface is not supported"); 371862306a36Sopenharmony_ci return -EOPNOTSUPP; 371962306a36Sopenharmony_ci } 372062306a36Sopenharmony_ci if (is_multicast_ether_addr(vxlan_fdb_info->eth_addr)) { 372162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Multicast MAC addresses not supported"); 372262306a36Sopenharmony_ci return -EOPNOTSUPP; 372362306a36Sopenharmony_ci } 372462306a36Sopenharmony_ci if (vxlan_addr_multicast(&vxlan_fdb_info->remote_ip)) { 372562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Multicast destination IP is not supported"); 372662306a36Sopenharmony_ci return -EOPNOTSUPP; 372762306a36Sopenharmony_ci } 372862306a36Sopenharmony_ci 372962306a36Sopenharmony_ci switchdev_work->vxlan_fdb_info = *vxlan_fdb_info; 373062306a36Sopenharmony_ci 373162306a36Sopenharmony_ci return 0; 373262306a36Sopenharmony_ci} 373362306a36Sopenharmony_ci 373462306a36Sopenharmony_ci/* Called under rcu_read_lock() */ 373562306a36Sopenharmony_cistatic int mlxsw_sp_switchdev_event(struct notifier_block *unused, 373662306a36Sopenharmony_ci unsigned long event, void *ptr) 373762306a36Sopenharmony_ci{ 373862306a36Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 373962306a36Sopenharmony_ci struct mlxsw_sp_switchdev_event_work *switchdev_work; 374062306a36Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info; 374162306a36Sopenharmony_ci struct switchdev_notifier_info *info = ptr; 374262306a36Sopenharmony_ci struct net_device *br_dev; 374362306a36Sopenharmony_ci int err; 374462306a36Sopenharmony_ci 374562306a36Sopenharmony_ci if (event == SWITCHDEV_PORT_ATTR_SET) { 374662306a36Sopenharmony_ci err = switchdev_handle_port_attr_set(dev, ptr, 374762306a36Sopenharmony_ci mlxsw_sp_port_dev_check, 374862306a36Sopenharmony_ci mlxsw_sp_port_attr_set); 374962306a36Sopenharmony_ci return notifier_from_errno(err); 375062306a36Sopenharmony_ci } 375162306a36Sopenharmony_ci 375262306a36Sopenharmony_ci /* Tunnel devices are not our uppers, so check their master instead */ 375362306a36Sopenharmony_ci br_dev = netdev_master_upper_dev_get_rcu(dev); 375462306a36Sopenharmony_ci if (!br_dev) 375562306a36Sopenharmony_ci return NOTIFY_DONE; 375662306a36Sopenharmony_ci if (!netif_is_bridge_master(br_dev)) 375762306a36Sopenharmony_ci return NOTIFY_DONE; 375862306a36Sopenharmony_ci if (!mlxsw_sp_port_dev_lower_find_rcu(br_dev)) 375962306a36Sopenharmony_ci return NOTIFY_DONE; 376062306a36Sopenharmony_ci 376162306a36Sopenharmony_ci switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); 376262306a36Sopenharmony_ci if (!switchdev_work) 376362306a36Sopenharmony_ci return NOTIFY_BAD; 376462306a36Sopenharmony_ci 376562306a36Sopenharmony_ci switchdev_work->dev = dev; 376662306a36Sopenharmony_ci switchdev_work->event = event; 376762306a36Sopenharmony_ci 376862306a36Sopenharmony_ci switch (event) { 376962306a36Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 377062306a36Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 377162306a36Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_BRIDGE: 377262306a36Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_BRIDGE: 377362306a36Sopenharmony_ci fdb_info = container_of(info, 377462306a36Sopenharmony_ci struct switchdev_notifier_fdb_info, 377562306a36Sopenharmony_ci info); 377662306a36Sopenharmony_ci INIT_WORK(&switchdev_work->work, 377762306a36Sopenharmony_ci mlxsw_sp_switchdev_bridge_fdb_event_work); 377862306a36Sopenharmony_ci memcpy(&switchdev_work->fdb_info, ptr, 377962306a36Sopenharmony_ci sizeof(switchdev_work->fdb_info)); 378062306a36Sopenharmony_ci switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); 378162306a36Sopenharmony_ci if (!switchdev_work->fdb_info.addr) 378262306a36Sopenharmony_ci goto err_addr_alloc; 378362306a36Sopenharmony_ci ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, 378462306a36Sopenharmony_ci fdb_info->addr); 378562306a36Sopenharmony_ci /* Take a reference on the device. This can be either 378662306a36Sopenharmony_ci * upper device containig mlxsw_sp_port or just a 378762306a36Sopenharmony_ci * mlxsw_sp_port 378862306a36Sopenharmony_ci */ 378962306a36Sopenharmony_ci netdev_hold(dev, &switchdev_work->dev_tracker, GFP_ATOMIC); 379062306a36Sopenharmony_ci break; 379162306a36Sopenharmony_ci case SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE: 379262306a36Sopenharmony_ci case SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE: 379362306a36Sopenharmony_ci INIT_WORK(&switchdev_work->work, 379462306a36Sopenharmony_ci mlxsw_sp_switchdev_vxlan_fdb_event_work); 379562306a36Sopenharmony_ci err = mlxsw_sp_switchdev_vxlan_work_prepare(switchdev_work, 379662306a36Sopenharmony_ci info); 379762306a36Sopenharmony_ci if (err) 379862306a36Sopenharmony_ci goto err_vxlan_work_prepare; 379962306a36Sopenharmony_ci netdev_hold(dev, &switchdev_work->dev_tracker, GFP_ATOMIC); 380062306a36Sopenharmony_ci break; 380162306a36Sopenharmony_ci default: 380262306a36Sopenharmony_ci kfree(switchdev_work); 380362306a36Sopenharmony_ci return NOTIFY_DONE; 380462306a36Sopenharmony_ci } 380562306a36Sopenharmony_ci 380662306a36Sopenharmony_ci mlxsw_core_schedule_work(&switchdev_work->work); 380762306a36Sopenharmony_ci 380862306a36Sopenharmony_ci return NOTIFY_DONE; 380962306a36Sopenharmony_ci 381062306a36Sopenharmony_cierr_vxlan_work_prepare: 381162306a36Sopenharmony_cierr_addr_alloc: 381262306a36Sopenharmony_ci kfree(switchdev_work); 381362306a36Sopenharmony_ci return NOTIFY_BAD; 381462306a36Sopenharmony_ci} 381562306a36Sopenharmony_ci 381662306a36Sopenharmony_cistruct notifier_block mlxsw_sp_switchdev_notifier = { 381762306a36Sopenharmony_ci .notifier_call = mlxsw_sp_switchdev_event, 381862306a36Sopenharmony_ci}; 381962306a36Sopenharmony_ci 382062306a36Sopenharmony_cistatic int 382162306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp, 382262306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 382362306a36Sopenharmony_ci const struct net_device *vxlan_dev, u16 vid, 382462306a36Sopenharmony_ci bool flag_untagged, bool flag_pvid, 382562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 382662306a36Sopenharmony_ci{ 382762306a36Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); 382862306a36Sopenharmony_ci __be32 vni = vxlan->cfg.vni; 382962306a36Sopenharmony_ci struct mlxsw_sp_fid *fid; 383062306a36Sopenharmony_ci u16 old_vid; 383162306a36Sopenharmony_ci int err; 383262306a36Sopenharmony_ci 383362306a36Sopenharmony_ci /* We cannot have the same VLAN as PVID and egress untagged on multiple 383462306a36Sopenharmony_ci * VxLAN devices. Note that we get this notification before the VLAN is 383562306a36Sopenharmony_ci * actually added to the bridge's database, so it is not possible for 383662306a36Sopenharmony_ci * the lookup function to return 'vxlan_dev' 383762306a36Sopenharmony_ci */ 383862306a36Sopenharmony_ci if (flag_untagged && flag_pvid && 383962306a36Sopenharmony_ci mlxsw_sp_bridge_8021q_vxlan_dev_find(bridge_device->dev, vid)) { 384062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VLAN already mapped to a different VNI"); 384162306a36Sopenharmony_ci return -EINVAL; 384262306a36Sopenharmony_ci } 384362306a36Sopenharmony_ci 384462306a36Sopenharmony_ci if (!netif_running(vxlan_dev)) 384562306a36Sopenharmony_ci return 0; 384662306a36Sopenharmony_ci 384762306a36Sopenharmony_ci /* First case: FID is not associated with this VNI, but the new VLAN 384862306a36Sopenharmony_ci * is both PVID and egress untagged. Need to enable NVE on the FID, if 384962306a36Sopenharmony_ci * it exists 385062306a36Sopenharmony_ci */ 385162306a36Sopenharmony_ci fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni); 385262306a36Sopenharmony_ci if (!fid) { 385362306a36Sopenharmony_ci if (!flag_untagged || !flag_pvid) 385462306a36Sopenharmony_ci return 0; 385562306a36Sopenharmony_ci return bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, 385662306a36Sopenharmony_ci vid, extack); 385762306a36Sopenharmony_ci } 385862306a36Sopenharmony_ci 385962306a36Sopenharmony_ci /* Second case: FID is associated with the VNI and the VLAN associated 386062306a36Sopenharmony_ci * with the FID is the same as the notified VLAN. This means the flags 386162306a36Sopenharmony_ci * (PVID / egress untagged) were toggled and that NVE should be 386262306a36Sopenharmony_ci * disabled on the FID 386362306a36Sopenharmony_ci */ 386462306a36Sopenharmony_ci old_vid = mlxsw_sp_fid_8021q_vid(fid); 386562306a36Sopenharmony_ci if (vid == old_vid) { 386662306a36Sopenharmony_ci if (WARN_ON(flag_untagged && flag_pvid)) { 386762306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 386862306a36Sopenharmony_ci return -EINVAL; 386962306a36Sopenharmony_ci } 387062306a36Sopenharmony_ci mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev); 387162306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 387262306a36Sopenharmony_ci return 0; 387362306a36Sopenharmony_ci } 387462306a36Sopenharmony_ci 387562306a36Sopenharmony_ci /* Third case: A new VLAN was configured on the VxLAN device, but this 387662306a36Sopenharmony_ci * VLAN is not PVID, so there is nothing to do. 387762306a36Sopenharmony_ci */ 387862306a36Sopenharmony_ci if (!flag_pvid) { 387962306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 388062306a36Sopenharmony_ci return 0; 388162306a36Sopenharmony_ci } 388262306a36Sopenharmony_ci 388362306a36Sopenharmony_ci /* Fourth case: Thew new VLAN is PVID, which means the VLAN currently 388462306a36Sopenharmony_ci * mapped to the VNI should be unmapped 388562306a36Sopenharmony_ci */ 388662306a36Sopenharmony_ci mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev); 388762306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 388862306a36Sopenharmony_ci 388962306a36Sopenharmony_ci /* Fifth case: The new VLAN is also egress untagged, which means the 389062306a36Sopenharmony_ci * VLAN needs to be mapped to the VNI 389162306a36Sopenharmony_ci */ 389262306a36Sopenharmony_ci if (!flag_untagged) 389362306a36Sopenharmony_ci return 0; 389462306a36Sopenharmony_ci 389562306a36Sopenharmony_ci err = bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, vid, extack); 389662306a36Sopenharmony_ci if (err) 389762306a36Sopenharmony_ci goto err_vxlan_join; 389862306a36Sopenharmony_ci 389962306a36Sopenharmony_ci return 0; 390062306a36Sopenharmony_ci 390162306a36Sopenharmony_cierr_vxlan_join: 390262306a36Sopenharmony_ci bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, old_vid, NULL); 390362306a36Sopenharmony_ci return err; 390462306a36Sopenharmony_ci} 390562306a36Sopenharmony_ci 390662306a36Sopenharmony_cistatic void 390762306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlan_del(struct mlxsw_sp *mlxsw_sp, 390862306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device, 390962306a36Sopenharmony_ci const struct net_device *vxlan_dev, u16 vid) 391062306a36Sopenharmony_ci{ 391162306a36Sopenharmony_ci struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); 391262306a36Sopenharmony_ci __be32 vni = vxlan->cfg.vni; 391362306a36Sopenharmony_ci struct mlxsw_sp_fid *fid; 391462306a36Sopenharmony_ci 391562306a36Sopenharmony_ci if (!netif_running(vxlan_dev)) 391662306a36Sopenharmony_ci return; 391762306a36Sopenharmony_ci 391862306a36Sopenharmony_ci fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni); 391962306a36Sopenharmony_ci if (!fid) 392062306a36Sopenharmony_ci return; 392162306a36Sopenharmony_ci 392262306a36Sopenharmony_ci /* A different VLAN than the one mapped to the VNI is deleted */ 392362306a36Sopenharmony_ci if (mlxsw_sp_fid_8021q_vid(fid) != vid) 392462306a36Sopenharmony_ci goto out; 392562306a36Sopenharmony_ci 392662306a36Sopenharmony_ci mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev); 392762306a36Sopenharmony_ci 392862306a36Sopenharmony_ciout: 392962306a36Sopenharmony_ci mlxsw_sp_fid_put(fid); 393062306a36Sopenharmony_ci} 393162306a36Sopenharmony_ci 393262306a36Sopenharmony_cistatic int 393362306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev, 393462306a36Sopenharmony_ci struct switchdev_notifier_port_obj_info * 393562306a36Sopenharmony_ci port_obj_info) 393662306a36Sopenharmony_ci{ 393762306a36Sopenharmony_ci struct switchdev_obj_port_vlan *vlan = 393862306a36Sopenharmony_ci SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj); 393962306a36Sopenharmony_ci bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 394062306a36Sopenharmony_ci bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; 394162306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 394262306a36Sopenharmony_ci struct netlink_ext_ack *extack; 394362306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 394462306a36Sopenharmony_ci struct net_device *br_dev; 394562306a36Sopenharmony_ci 394662306a36Sopenharmony_ci extack = switchdev_notifier_info_to_extack(&port_obj_info->info); 394762306a36Sopenharmony_ci br_dev = netdev_master_upper_dev_get(vxlan_dev); 394862306a36Sopenharmony_ci if (!br_dev) 394962306a36Sopenharmony_ci return 0; 395062306a36Sopenharmony_ci 395162306a36Sopenharmony_ci mlxsw_sp = mlxsw_sp_lower_get(br_dev); 395262306a36Sopenharmony_ci if (!mlxsw_sp) 395362306a36Sopenharmony_ci return 0; 395462306a36Sopenharmony_ci 395562306a36Sopenharmony_ci port_obj_info->handled = true; 395662306a36Sopenharmony_ci 395762306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 395862306a36Sopenharmony_ci if (!bridge_device) 395962306a36Sopenharmony_ci return -EINVAL; 396062306a36Sopenharmony_ci 396162306a36Sopenharmony_ci if (!bridge_device->vlan_enabled) 396262306a36Sopenharmony_ci return 0; 396362306a36Sopenharmony_ci 396462306a36Sopenharmony_ci return mlxsw_sp_switchdev_vxlan_vlan_add(mlxsw_sp, bridge_device, 396562306a36Sopenharmony_ci vxlan_dev, vlan->vid, 396662306a36Sopenharmony_ci flag_untagged, 396762306a36Sopenharmony_ci flag_pvid, extack); 396862306a36Sopenharmony_ci} 396962306a36Sopenharmony_ci 397062306a36Sopenharmony_cistatic void 397162306a36Sopenharmony_cimlxsw_sp_switchdev_vxlan_vlans_del(struct net_device *vxlan_dev, 397262306a36Sopenharmony_ci struct switchdev_notifier_port_obj_info * 397362306a36Sopenharmony_ci port_obj_info) 397462306a36Sopenharmony_ci{ 397562306a36Sopenharmony_ci struct switchdev_obj_port_vlan *vlan = 397662306a36Sopenharmony_ci SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj); 397762306a36Sopenharmony_ci struct mlxsw_sp_bridge_device *bridge_device; 397862306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 397962306a36Sopenharmony_ci struct net_device *br_dev; 398062306a36Sopenharmony_ci 398162306a36Sopenharmony_ci br_dev = netdev_master_upper_dev_get(vxlan_dev); 398262306a36Sopenharmony_ci if (!br_dev) 398362306a36Sopenharmony_ci return; 398462306a36Sopenharmony_ci 398562306a36Sopenharmony_ci mlxsw_sp = mlxsw_sp_lower_get(br_dev); 398662306a36Sopenharmony_ci if (!mlxsw_sp) 398762306a36Sopenharmony_ci return; 398862306a36Sopenharmony_ci 398962306a36Sopenharmony_ci port_obj_info->handled = true; 399062306a36Sopenharmony_ci 399162306a36Sopenharmony_ci bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); 399262306a36Sopenharmony_ci if (!bridge_device) 399362306a36Sopenharmony_ci return; 399462306a36Sopenharmony_ci 399562306a36Sopenharmony_ci if (!bridge_device->vlan_enabled) 399662306a36Sopenharmony_ci return; 399762306a36Sopenharmony_ci 399862306a36Sopenharmony_ci mlxsw_sp_switchdev_vxlan_vlan_del(mlxsw_sp, bridge_device, vxlan_dev, 399962306a36Sopenharmony_ci vlan->vid); 400062306a36Sopenharmony_ci} 400162306a36Sopenharmony_ci 400262306a36Sopenharmony_cistatic int 400362306a36Sopenharmony_cimlxsw_sp_switchdev_handle_vxlan_obj_add(struct net_device *vxlan_dev, 400462306a36Sopenharmony_ci struct switchdev_notifier_port_obj_info * 400562306a36Sopenharmony_ci port_obj_info) 400662306a36Sopenharmony_ci{ 400762306a36Sopenharmony_ci int err = 0; 400862306a36Sopenharmony_ci 400962306a36Sopenharmony_ci switch (port_obj_info->obj->id) { 401062306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 401162306a36Sopenharmony_ci err = mlxsw_sp_switchdev_vxlan_vlans_add(vxlan_dev, 401262306a36Sopenharmony_ci port_obj_info); 401362306a36Sopenharmony_ci break; 401462306a36Sopenharmony_ci default: 401562306a36Sopenharmony_ci break; 401662306a36Sopenharmony_ci } 401762306a36Sopenharmony_ci 401862306a36Sopenharmony_ci return err; 401962306a36Sopenharmony_ci} 402062306a36Sopenharmony_ci 402162306a36Sopenharmony_cistatic void 402262306a36Sopenharmony_cimlxsw_sp_switchdev_handle_vxlan_obj_del(struct net_device *vxlan_dev, 402362306a36Sopenharmony_ci struct switchdev_notifier_port_obj_info * 402462306a36Sopenharmony_ci port_obj_info) 402562306a36Sopenharmony_ci{ 402662306a36Sopenharmony_ci switch (port_obj_info->obj->id) { 402762306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 402862306a36Sopenharmony_ci mlxsw_sp_switchdev_vxlan_vlans_del(vxlan_dev, port_obj_info); 402962306a36Sopenharmony_ci break; 403062306a36Sopenharmony_ci default: 403162306a36Sopenharmony_ci break; 403262306a36Sopenharmony_ci } 403362306a36Sopenharmony_ci} 403462306a36Sopenharmony_ci 403562306a36Sopenharmony_cistatic int mlxsw_sp_switchdev_blocking_event(struct notifier_block *unused, 403662306a36Sopenharmony_ci unsigned long event, void *ptr) 403762306a36Sopenharmony_ci{ 403862306a36Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 403962306a36Sopenharmony_ci int err = 0; 404062306a36Sopenharmony_ci 404162306a36Sopenharmony_ci switch (event) { 404262306a36Sopenharmony_ci case SWITCHDEV_PORT_OBJ_ADD: 404362306a36Sopenharmony_ci if (netif_is_vxlan(dev)) 404462306a36Sopenharmony_ci err = mlxsw_sp_switchdev_handle_vxlan_obj_add(dev, ptr); 404562306a36Sopenharmony_ci else 404662306a36Sopenharmony_ci err = switchdev_handle_port_obj_add(dev, ptr, 404762306a36Sopenharmony_ci mlxsw_sp_port_dev_check, 404862306a36Sopenharmony_ci mlxsw_sp_port_obj_add); 404962306a36Sopenharmony_ci return notifier_from_errno(err); 405062306a36Sopenharmony_ci case SWITCHDEV_PORT_OBJ_DEL: 405162306a36Sopenharmony_ci if (netif_is_vxlan(dev)) 405262306a36Sopenharmony_ci mlxsw_sp_switchdev_handle_vxlan_obj_del(dev, ptr); 405362306a36Sopenharmony_ci else 405462306a36Sopenharmony_ci err = switchdev_handle_port_obj_del(dev, ptr, 405562306a36Sopenharmony_ci mlxsw_sp_port_dev_check, 405662306a36Sopenharmony_ci mlxsw_sp_port_obj_del); 405762306a36Sopenharmony_ci return notifier_from_errno(err); 405862306a36Sopenharmony_ci case SWITCHDEV_PORT_ATTR_SET: 405962306a36Sopenharmony_ci err = switchdev_handle_port_attr_set(dev, ptr, 406062306a36Sopenharmony_ci mlxsw_sp_port_dev_check, 406162306a36Sopenharmony_ci mlxsw_sp_port_attr_set); 406262306a36Sopenharmony_ci return notifier_from_errno(err); 406362306a36Sopenharmony_ci } 406462306a36Sopenharmony_ci 406562306a36Sopenharmony_ci return NOTIFY_DONE; 406662306a36Sopenharmony_ci} 406762306a36Sopenharmony_ci 406862306a36Sopenharmony_cistatic struct notifier_block mlxsw_sp_switchdev_blocking_notifier = { 406962306a36Sopenharmony_ci .notifier_call = mlxsw_sp_switchdev_blocking_event, 407062306a36Sopenharmony_ci}; 407162306a36Sopenharmony_ci 407262306a36Sopenharmony_ciu8 407362306a36Sopenharmony_cimlxsw_sp_bridge_port_stp_state(struct mlxsw_sp_bridge_port *bridge_port) 407462306a36Sopenharmony_ci{ 407562306a36Sopenharmony_ci return bridge_port->stp_state; 407662306a36Sopenharmony_ci} 407762306a36Sopenharmony_ci 407862306a36Sopenharmony_cistatic int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp) 407962306a36Sopenharmony_ci{ 408062306a36Sopenharmony_ci struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge; 408162306a36Sopenharmony_ci struct notifier_block *nb; 408262306a36Sopenharmony_ci int err; 408362306a36Sopenharmony_ci 408462306a36Sopenharmony_ci err = mlxsw_sp_ageing_set(mlxsw_sp, MLXSW_SP_DEFAULT_AGEING_TIME); 408562306a36Sopenharmony_ci if (err) { 408662306a36Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, "Failed to set default ageing time\n"); 408762306a36Sopenharmony_ci return err; 408862306a36Sopenharmony_ci } 408962306a36Sopenharmony_ci 409062306a36Sopenharmony_ci err = register_switchdev_notifier(&mlxsw_sp_switchdev_notifier); 409162306a36Sopenharmony_ci if (err) { 409262306a36Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, "Failed to register switchdev notifier\n"); 409362306a36Sopenharmony_ci return err; 409462306a36Sopenharmony_ci } 409562306a36Sopenharmony_ci 409662306a36Sopenharmony_ci nb = &mlxsw_sp_switchdev_blocking_notifier; 409762306a36Sopenharmony_ci err = register_switchdev_blocking_notifier(nb); 409862306a36Sopenharmony_ci if (err) { 409962306a36Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, "Failed to register switchdev blocking notifier\n"); 410062306a36Sopenharmony_ci goto err_register_switchdev_blocking_notifier; 410162306a36Sopenharmony_ci } 410262306a36Sopenharmony_ci 410362306a36Sopenharmony_ci INIT_DELAYED_WORK(&bridge->fdb_notify.dw, mlxsw_sp_fdb_notify_work); 410462306a36Sopenharmony_ci bridge->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL; 410562306a36Sopenharmony_ci return 0; 410662306a36Sopenharmony_ci 410762306a36Sopenharmony_cierr_register_switchdev_blocking_notifier: 410862306a36Sopenharmony_ci unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier); 410962306a36Sopenharmony_ci return err; 411062306a36Sopenharmony_ci} 411162306a36Sopenharmony_ci 411262306a36Sopenharmony_cistatic void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp) 411362306a36Sopenharmony_ci{ 411462306a36Sopenharmony_ci struct notifier_block *nb; 411562306a36Sopenharmony_ci 411662306a36Sopenharmony_ci cancel_delayed_work_sync(&mlxsw_sp->bridge->fdb_notify.dw); 411762306a36Sopenharmony_ci 411862306a36Sopenharmony_ci nb = &mlxsw_sp_switchdev_blocking_notifier; 411962306a36Sopenharmony_ci unregister_switchdev_blocking_notifier(nb); 412062306a36Sopenharmony_ci 412162306a36Sopenharmony_ci unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier); 412262306a36Sopenharmony_ci} 412362306a36Sopenharmony_ci 412462306a36Sopenharmony_cistatic void mlxsw_sp1_switchdev_init(struct mlxsw_sp *mlxsw_sp) 412562306a36Sopenharmony_ci{ 412662306a36Sopenharmony_ci mlxsw_sp->bridge->bridge_8021ad_ops = &mlxsw_sp1_bridge_8021ad_ops; 412762306a36Sopenharmony_ci} 412862306a36Sopenharmony_ci 412962306a36Sopenharmony_ciconst struct mlxsw_sp_switchdev_ops mlxsw_sp1_switchdev_ops = { 413062306a36Sopenharmony_ci .init = mlxsw_sp1_switchdev_init, 413162306a36Sopenharmony_ci}; 413262306a36Sopenharmony_ci 413362306a36Sopenharmony_cistatic void mlxsw_sp2_switchdev_init(struct mlxsw_sp *mlxsw_sp) 413462306a36Sopenharmony_ci{ 413562306a36Sopenharmony_ci mlxsw_sp->bridge->bridge_8021ad_ops = &mlxsw_sp2_bridge_8021ad_ops; 413662306a36Sopenharmony_ci} 413762306a36Sopenharmony_ci 413862306a36Sopenharmony_ciconst struct mlxsw_sp_switchdev_ops mlxsw_sp2_switchdev_ops = { 413962306a36Sopenharmony_ci .init = mlxsw_sp2_switchdev_init, 414062306a36Sopenharmony_ci}; 414162306a36Sopenharmony_ci 414262306a36Sopenharmony_ciint mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp) 414362306a36Sopenharmony_ci{ 414462306a36Sopenharmony_ci struct mlxsw_sp_bridge *bridge; 414562306a36Sopenharmony_ci 414662306a36Sopenharmony_ci bridge = kzalloc(sizeof(*mlxsw_sp->bridge), GFP_KERNEL); 414762306a36Sopenharmony_ci if (!bridge) 414862306a36Sopenharmony_ci return -ENOMEM; 414962306a36Sopenharmony_ci mlxsw_sp->bridge = bridge; 415062306a36Sopenharmony_ci bridge->mlxsw_sp = mlxsw_sp; 415162306a36Sopenharmony_ci 415262306a36Sopenharmony_ci INIT_LIST_HEAD(&mlxsw_sp->bridge->bridges_list); 415362306a36Sopenharmony_ci 415462306a36Sopenharmony_ci bridge->bridge_8021q_ops = &mlxsw_sp_bridge_8021q_ops; 415562306a36Sopenharmony_ci bridge->bridge_8021d_ops = &mlxsw_sp_bridge_8021d_ops; 415662306a36Sopenharmony_ci 415762306a36Sopenharmony_ci mlxsw_sp->switchdev_ops->init(mlxsw_sp); 415862306a36Sopenharmony_ci 415962306a36Sopenharmony_ci return mlxsw_sp_fdb_init(mlxsw_sp); 416062306a36Sopenharmony_ci} 416162306a36Sopenharmony_ci 416262306a36Sopenharmony_civoid mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp) 416362306a36Sopenharmony_ci{ 416462306a36Sopenharmony_ci mlxsw_sp_fdb_fini(mlxsw_sp); 416562306a36Sopenharmony_ci WARN_ON(!list_empty(&mlxsw_sp->bridge->bridges_list)); 416662306a36Sopenharmony_ci kfree(mlxsw_sp->bridge); 416762306a36Sopenharmony_ci} 416862306a36Sopenharmony_ci 4169