162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/if_bridge.h> 562306a36Sopenharmony_ci#include <linux/if_vlan.h> 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/notifier.h> 962306a36Sopenharmony_ci#include <net/netevent.h> 1062306a36Sopenharmony_ci#include <net/switchdev.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "prestera.h" 1362306a36Sopenharmony_ci#include "prestera_hw.h" 1462306a36Sopenharmony_ci#include "prestera_switchdev.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define PRESTERA_VID_ALL (0xffff) 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define PRESTERA_DEFAULT_AGEING_TIME_MS 300000 1962306a36Sopenharmony_ci#define PRESTERA_MAX_AGEING_TIME_MS 1000000000 2062306a36Sopenharmony_ci#define PRESTERA_MIN_AGEING_TIME_MS 32000 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct prestera_fdb_event_work { 2362306a36Sopenharmony_ci struct work_struct work; 2462306a36Sopenharmony_ci struct switchdev_notifier_fdb_info fdb_info; 2562306a36Sopenharmony_ci struct net_device *dev; 2662306a36Sopenharmony_ci unsigned long event; 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct prestera_switchdev { 3062306a36Sopenharmony_ci struct prestera_switch *sw; 3162306a36Sopenharmony_ci struct list_head bridge_list; 3262306a36Sopenharmony_ci bool bridge_8021q_exists; 3362306a36Sopenharmony_ci struct notifier_block swdev_nb_blk; 3462306a36Sopenharmony_ci struct notifier_block swdev_nb; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct prestera_bridge { 3862306a36Sopenharmony_ci struct list_head head; 3962306a36Sopenharmony_ci struct net_device *dev; 4062306a36Sopenharmony_ci struct prestera_switchdev *swdev; 4162306a36Sopenharmony_ci struct list_head port_list; 4262306a36Sopenharmony_ci struct list_head br_mdb_entry_list; 4362306a36Sopenharmony_ci bool mrouter_exist; 4462306a36Sopenharmony_ci bool vlan_enabled; 4562306a36Sopenharmony_ci bool multicast_enabled; 4662306a36Sopenharmony_ci u16 bridge_id; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct prestera_bridge_port { 5062306a36Sopenharmony_ci struct list_head head; 5162306a36Sopenharmony_ci struct net_device *dev; 5262306a36Sopenharmony_ci struct prestera_bridge *bridge; 5362306a36Sopenharmony_ci struct list_head vlan_list; 5462306a36Sopenharmony_ci struct list_head br_mdb_port_list; 5562306a36Sopenharmony_ci refcount_t ref_count; 5662306a36Sopenharmony_ci unsigned long flags; 5762306a36Sopenharmony_ci bool mrouter; 5862306a36Sopenharmony_ci u8 stp_state; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct prestera_bridge_vlan { 6262306a36Sopenharmony_ci struct list_head head; 6362306a36Sopenharmony_ci struct list_head port_vlan_list; 6462306a36Sopenharmony_ci u16 vid; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct prestera_port_vlan { 6862306a36Sopenharmony_ci struct list_head br_vlan_head; 6962306a36Sopenharmony_ci struct list_head port_head; 7062306a36Sopenharmony_ci struct prestera_port *port; 7162306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 7262306a36Sopenharmony_ci u16 vid; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistruct prestera_br_mdb_port { 7662306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 7762306a36Sopenharmony_ci struct list_head br_mdb_port_node; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* Software representation of MDB table. */ 8162306a36Sopenharmony_cistruct prestera_br_mdb_entry { 8262306a36Sopenharmony_ci struct prestera_bridge *bridge; 8362306a36Sopenharmony_ci struct prestera_mdb_entry *mdb; 8462306a36Sopenharmony_ci struct list_head br_mdb_port_list; 8562306a36Sopenharmony_ci struct list_head br_mdb_entry_node; 8662306a36Sopenharmony_ci bool enabled; 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic struct workqueue_struct *swdev_wq; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void prestera_bridge_port_put(struct prestera_bridge_port *br_port); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int prestera_port_vid_stp_set(struct prestera_port *port, u16 vid, 9462306a36Sopenharmony_ci u8 state); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic struct prestera_bridge * 9762306a36Sopenharmony_ciprestera_bridge_find(const struct prestera_switch *sw, 9862306a36Sopenharmony_ci const struct net_device *br_dev) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct prestera_bridge *bridge; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci list_for_each_entry(bridge, &sw->swdev->bridge_list, head) 10362306a36Sopenharmony_ci if (bridge->dev == br_dev) 10462306a36Sopenharmony_ci return bridge; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return NULL; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic struct prestera_bridge_port * 11062306a36Sopenharmony_ci__prestera_bridge_port_find(const struct prestera_bridge *bridge, 11162306a36Sopenharmony_ci const struct net_device *brport_dev) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci list_for_each_entry(br_port, &bridge->port_list, head) 11662306a36Sopenharmony_ci if (br_port->dev == brport_dev) 11762306a36Sopenharmony_ci return br_port; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return NULL; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic struct prestera_bridge_port * 12362306a36Sopenharmony_ciprestera_bridge_port_find(struct prestera_switch *sw, 12462306a36Sopenharmony_ci struct net_device *brport_dev) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct net_device *br_dev = netdev_master_upper_dev_get(brport_dev); 12762306a36Sopenharmony_ci struct prestera_bridge *bridge; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (!br_dev) 13062306a36Sopenharmony_ci return NULL; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci bridge = prestera_bridge_find(sw, br_dev); 13362306a36Sopenharmony_ci if (!bridge) 13462306a36Sopenharmony_ci return NULL; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return __prestera_bridge_port_find(bridge, brport_dev); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void 14062306a36Sopenharmony_ciprestera_br_port_flags_reset(struct prestera_bridge_port *br_port, 14162306a36Sopenharmony_ci struct prestera_port *port) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci prestera_port_uc_flood_set(port, false); 14462306a36Sopenharmony_ci prestera_port_mc_flood_set(port, false); 14562306a36Sopenharmony_ci prestera_port_learning_set(port, false); 14662306a36Sopenharmony_ci prestera_port_br_locked_set(port, false); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int prestera_br_port_flags_set(struct prestera_bridge_port *br_port, 15062306a36Sopenharmony_ci struct prestera_port *port) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci int err; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci err = prestera_port_uc_flood_set(port, br_port->flags & BR_FLOOD); 15562306a36Sopenharmony_ci if (err) 15662306a36Sopenharmony_ci goto err_out; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci err = prestera_port_mc_flood_set(port, br_port->flags & BR_MCAST_FLOOD); 15962306a36Sopenharmony_ci if (err) 16062306a36Sopenharmony_ci goto err_out; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci err = prestera_port_learning_set(port, br_port->flags & BR_LEARNING); 16362306a36Sopenharmony_ci if (err) 16462306a36Sopenharmony_ci goto err_out; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci err = prestera_port_br_locked_set(port, 16762306a36Sopenharmony_ci br_port->flags & BR_PORT_LOCKED); 16862306a36Sopenharmony_ci if (err) 16962306a36Sopenharmony_ci goto err_out; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cierr_out: 17462306a36Sopenharmony_ci prestera_br_port_flags_reset(br_port, port); 17562306a36Sopenharmony_ci return err; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic struct prestera_bridge_vlan * 17962306a36Sopenharmony_ciprestera_bridge_vlan_create(struct prestera_bridge_port *br_port, u16 vid) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct prestera_bridge_vlan *br_vlan; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci br_vlan = kzalloc(sizeof(*br_vlan), GFP_KERNEL); 18462306a36Sopenharmony_ci if (!br_vlan) 18562306a36Sopenharmony_ci return NULL; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci INIT_LIST_HEAD(&br_vlan->port_vlan_list); 18862306a36Sopenharmony_ci br_vlan->vid = vid; 18962306a36Sopenharmony_ci list_add(&br_vlan->head, &br_port->vlan_list); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return br_vlan; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void prestera_bridge_vlan_destroy(struct prestera_bridge_vlan *br_vlan) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci list_del(&br_vlan->head); 19762306a36Sopenharmony_ci WARN_ON(!list_empty(&br_vlan->port_vlan_list)); 19862306a36Sopenharmony_ci kfree(br_vlan); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic struct prestera_bridge_vlan * 20262306a36Sopenharmony_ciprestera_bridge_vlan_by_vid(struct prestera_bridge_port *br_port, u16 vid) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct prestera_bridge_vlan *br_vlan; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci list_for_each_entry(br_vlan, &br_port->vlan_list, head) { 20762306a36Sopenharmony_ci if (br_vlan->vid == vid) 20862306a36Sopenharmony_ci return br_vlan; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return NULL; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic int prestera_bridge_vlan_port_count(struct prestera_bridge *bridge, 21562306a36Sopenharmony_ci u16 vid) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 21862306a36Sopenharmony_ci struct prestera_bridge_vlan *br_vlan; 21962306a36Sopenharmony_ci int count = 0; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci list_for_each_entry(br_port, &bridge->port_list, head) { 22262306a36Sopenharmony_ci list_for_each_entry(br_vlan, &br_port->vlan_list, head) { 22362306a36Sopenharmony_ci if (br_vlan->vid == vid) { 22462306a36Sopenharmony_ci count += 1; 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return count; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic void prestera_bridge_vlan_put(struct prestera_bridge_vlan *br_vlan) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci if (list_empty(&br_vlan->port_vlan_list)) 23662306a36Sopenharmony_ci prestera_bridge_vlan_destroy(br_vlan); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic struct prestera_port_vlan * 24062306a36Sopenharmony_ciprestera_port_vlan_by_vid(struct prestera_port *port, u16 vid) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct prestera_port_vlan *port_vlan; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci list_for_each_entry(port_vlan, &port->vlans_list, port_head) { 24562306a36Sopenharmony_ci if (port_vlan->vid == vid) 24662306a36Sopenharmony_ci return port_vlan; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return NULL; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic struct prestera_port_vlan * 25362306a36Sopenharmony_ciprestera_port_vlan_create(struct prestera_port *port, u16 vid, bool untagged) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct prestera_port_vlan *port_vlan; 25662306a36Sopenharmony_ci int err; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci port_vlan = prestera_port_vlan_by_vid(port, vid); 25962306a36Sopenharmony_ci if (port_vlan) 26062306a36Sopenharmony_ci return ERR_PTR(-EEXIST); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci err = prestera_hw_vlan_port_set(port, vid, true, untagged); 26362306a36Sopenharmony_ci if (err) 26462306a36Sopenharmony_ci return ERR_PTR(err); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci port_vlan = kzalloc(sizeof(*port_vlan), GFP_KERNEL); 26762306a36Sopenharmony_ci if (!port_vlan) { 26862306a36Sopenharmony_ci err = -ENOMEM; 26962306a36Sopenharmony_ci goto err_port_vlan_alloc; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci port_vlan->port = port; 27362306a36Sopenharmony_ci port_vlan->vid = vid; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci list_add(&port_vlan->port_head, &port->vlans_list); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return port_vlan; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cierr_port_vlan_alloc: 28062306a36Sopenharmony_ci prestera_hw_vlan_port_set(port, vid, false, false); 28162306a36Sopenharmony_ci return ERR_PTR(err); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int prestera_fdb_add(struct prestera_port *port, 28562306a36Sopenharmony_ci const unsigned char *mac, u16 vid, bool dynamic) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci if (prestera_port_is_lag_member(port)) 28862306a36Sopenharmony_ci return prestera_hw_lag_fdb_add(port->sw, prestera_port_lag_id(port), 28962306a36Sopenharmony_ci mac, vid, dynamic); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return prestera_hw_fdb_add(port, mac, vid, dynamic); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int prestera_fdb_del(struct prestera_port *port, 29562306a36Sopenharmony_ci const unsigned char *mac, u16 vid) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci if (prestera_port_is_lag_member(port)) 29862306a36Sopenharmony_ci return prestera_hw_lag_fdb_del(port->sw, prestera_port_lag_id(port), 29962306a36Sopenharmony_ci mac, vid); 30062306a36Sopenharmony_ci else 30162306a36Sopenharmony_ci return prestera_hw_fdb_del(port, mac, vid); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int prestera_fdb_flush_port_vlan(struct prestera_port *port, u16 vid, 30562306a36Sopenharmony_ci u32 mode) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci if (prestera_port_is_lag_member(port)) 30862306a36Sopenharmony_ci return prestera_hw_fdb_flush_lag_vlan(port->sw, prestera_port_lag_id(port), 30962306a36Sopenharmony_ci vid, mode); 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci return prestera_hw_fdb_flush_port_vlan(port, vid, mode); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int prestera_fdb_flush_port(struct prestera_port *port, u32 mode) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci if (prestera_port_is_lag_member(port)) 31762306a36Sopenharmony_ci return prestera_hw_fdb_flush_lag(port->sw, prestera_port_lag_id(port), 31862306a36Sopenharmony_ci mode); 31962306a36Sopenharmony_ci else 32062306a36Sopenharmony_ci return prestera_hw_fdb_flush_port(port, mode); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic void 32462306a36Sopenharmony_ciprestera_mdb_port_del(struct prestera_mdb_entry *mdb, 32562306a36Sopenharmony_ci struct net_device *orig_dev) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct prestera_flood_domain *fl_domain = mdb->flood_domain; 32862306a36Sopenharmony_ci struct prestera_flood_domain_port *flood_domain_port; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci flood_domain_port = prestera_flood_domain_port_find(fl_domain, 33162306a36Sopenharmony_ci orig_dev, 33262306a36Sopenharmony_ci mdb->vid); 33362306a36Sopenharmony_ci if (flood_domain_port) 33462306a36Sopenharmony_ci prestera_flood_domain_port_destroy(flood_domain_port); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void 33862306a36Sopenharmony_ciprestera_br_mdb_entry_put(struct prestera_br_mdb_entry *br_mdb) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (list_empty(&br_mdb->br_mdb_port_list)) { 34362306a36Sopenharmony_ci list_for_each_entry(br_port, &br_mdb->bridge->port_list, head) 34462306a36Sopenharmony_ci prestera_mdb_port_del(br_mdb->mdb, br_port->dev); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci prestera_mdb_entry_destroy(br_mdb->mdb); 34762306a36Sopenharmony_ci list_del(&br_mdb->br_mdb_entry_node); 34862306a36Sopenharmony_ci kfree(br_mdb); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic void 35362306a36Sopenharmony_ciprestera_br_mdb_port_del(struct prestera_br_mdb_entry *br_mdb, 35462306a36Sopenharmony_ci struct prestera_bridge_port *br_port) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct prestera_br_mdb_port *br_mdb_port, *tmp; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci list_for_each_entry_safe(br_mdb_port, tmp, &br_mdb->br_mdb_port_list, 35962306a36Sopenharmony_ci br_mdb_port_node) { 36062306a36Sopenharmony_ci if (br_mdb_port->br_port == br_port) { 36162306a36Sopenharmony_ci list_del(&br_mdb_port->br_mdb_port_node); 36262306a36Sopenharmony_ci kfree(br_mdb_port); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic void 36862306a36Sopenharmony_ciprestera_mdb_flush_bridge_port(struct prestera_bridge_port *br_port) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct prestera_br_mdb_port *br_mdb_port, *tmp_port; 37162306a36Sopenharmony_ci struct prestera_br_mdb_entry *br_mdb, *br_mdb_tmp; 37262306a36Sopenharmony_ci struct prestera_bridge *br_dev = br_port->bridge; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci list_for_each_entry_safe(br_mdb, br_mdb_tmp, &br_dev->br_mdb_entry_list, 37562306a36Sopenharmony_ci br_mdb_entry_node) { 37662306a36Sopenharmony_ci list_for_each_entry_safe(br_mdb_port, tmp_port, 37762306a36Sopenharmony_ci &br_mdb->br_mdb_port_list, 37862306a36Sopenharmony_ci br_mdb_port_node) { 37962306a36Sopenharmony_ci prestera_mdb_port_del(br_mdb->mdb, 38062306a36Sopenharmony_ci br_mdb_port->br_port->dev); 38162306a36Sopenharmony_ci prestera_br_mdb_port_del(br_mdb, br_mdb_port->br_port); 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci prestera_br_mdb_entry_put(br_mdb); 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic void 38862306a36Sopenharmony_ciprestera_port_vlan_bridge_leave(struct prestera_port_vlan *port_vlan) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci u32 fdb_flush_mode = PRESTERA_FDB_FLUSH_MODE_DYNAMIC; 39162306a36Sopenharmony_ci struct prestera_port *port = port_vlan->port; 39262306a36Sopenharmony_ci struct prestera_bridge_vlan *br_vlan; 39362306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 39462306a36Sopenharmony_ci bool last_port, last_vlan; 39562306a36Sopenharmony_ci u16 vid = port_vlan->vid; 39662306a36Sopenharmony_ci int port_count; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci br_port = port_vlan->br_port; 39962306a36Sopenharmony_ci port_count = prestera_bridge_vlan_port_count(br_port->bridge, vid); 40062306a36Sopenharmony_ci br_vlan = prestera_bridge_vlan_by_vid(br_port, vid); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci last_vlan = list_is_singular(&br_port->vlan_list); 40362306a36Sopenharmony_ci last_port = port_count == 1; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (last_vlan) 40662306a36Sopenharmony_ci prestera_fdb_flush_port(port, fdb_flush_mode); 40762306a36Sopenharmony_ci else if (last_port) 40862306a36Sopenharmony_ci prestera_hw_fdb_flush_vlan(port->sw, vid, fdb_flush_mode); 40962306a36Sopenharmony_ci else 41062306a36Sopenharmony_ci prestera_fdb_flush_port_vlan(port, vid, fdb_flush_mode); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci prestera_mdb_flush_bridge_port(br_port); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci list_del(&port_vlan->br_vlan_head); 41562306a36Sopenharmony_ci prestera_bridge_vlan_put(br_vlan); 41662306a36Sopenharmony_ci prestera_bridge_port_put(br_port); 41762306a36Sopenharmony_ci port_vlan->br_port = NULL; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic void prestera_port_vlan_destroy(struct prestera_port_vlan *port_vlan) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct prestera_port *port = port_vlan->port; 42362306a36Sopenharmony_ci u16 vid = port_vlan->vid; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (port_vlan->br_port) 42662306a36Sopenharmony_ci prestera_port_vlan_bridge_leave(port_vlan); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci prestera_hw_vlan_port_set(port, vid, false, false); 42962306a36Sopenharmony_ci list_del(&port_vlan->port_head); 43062306a36Sopenharmony_ci kfree(port_vlan); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic struct prestera_bridge * 43462306a36Sopenharmony_ciprestera_bridge_create(struct prestera_switchdev *swdev, struct net_device *dev) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci bool vlan_enabled = br_vlan_enabled(dev); 43762306a36Sopenharmony_ci struct prestera_bridge *bridge; 43862306a36Sopenharmony_ci u16 bridge_id; 43962306a36Sopenharmony_ci int err; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (vlan_enabled && swdev->bridge_8021q_exists) { 44262306a36Sopenharmony_ci netdev_err(dev, "Only one VLAN-aware bridge is supported\n"); 44362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); 44762306a36Sopenharmony_ci if (!bridge) 44862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (vlan_enabled) { 45162306a36Sopenharmony_ci swdev->bridge_8021q_exists = true; 45262306a36Sopenharmony_ci } else { 45362306a36Sopenharmony_ci err = prestera_hw_bridge_create(swdev->sw, &bridge_id); 45462306a36Sopenharmony_ci if (err) { 45562306a36Sopenharmony_ci kfree(bridge); 45662306a36Sopenharmony_ci return ERR_PTR(err); 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci bridge->bridge_id = bridge_id; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci bridge->vlan_enabled = vlan_enabled; 46362306a36Sopenharmony_ci bridge->swdev = swdev; 46462306a36Sopenharmony_ci bridge->dev = dev; 46562306a36Sopenharmony_ci bridge->multicast_enabled = br_multicast_enabled(dev); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci INIT_LIST_HEAD(&bridge->port_list); 46862306a36Sopenharmony_ci INIT_LIST_HEAD(&bridge->br_mdb_entry_list); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci list_add(&bridge->head, &swdev->bridge_list); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return bridge; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic void prestera_bridge_destroy(struct prestera_bridge *bridge) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct prestera_switchdev *swdev = bridge->swdev; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci list_del(&bridge->head); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (bridge->vlan_enabled) 48262306a36Sopenharmony_ci swdev->bridge_8021q_exists = false; 48362306a36Sopenharmony_ci else 48462306a36Sopenharmony_ci prestera_hw_bridge_delete(swdev->sw, bridge->bridge_id); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci WARN_ON(!list_empty(&bridge->br_mdb_entry_list)); 48762306a36Sopenharmony_ci WARN_ON(!list_empty(&bridge->port_list)); 48862306a36Sopenharmony_ci kfree(bridge); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic void prestera_bridge_put(struct prestera_bridge *bridge) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci if (list_empty(&bridge->port_list)) 49462306a36Sopenharmony_ci prestera_bridge_destroy(bridge); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic 49862306a36Sopenharmony_cistruct prestera_bridge *prestera_bridge_by_dev(struct prestera_switchdev *swdev, 49962306a36Sopenharmony_ci const struct net_device *dev) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct prestera_bridge *bridge; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci list_for_each_entry(bridge, &swdev->bridge_list, head) 50462306a36Sopenharmony_ci if (bridge->dev == dev) 50562306a36Sopenharmony_ci return bridge; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return NULL; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic struct prestera_bridge_port * 51162306a36Sopenharmony_ci__prestera_bridge_port_by_dev(struct prestera_bridge *bridge, 51262306a36Sopenharmony_ci struct net_device *dev) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci list_for_each_entry(br_port, &bridge->port_list, head) { 51762306a36Sopenharmony_ci if (br_port->dev == dev) 51862306a36Sopenharmony_ci return br_port; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return NULL; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic int prestera_match_upper_bridge_dev(struct net_device *dev, 52562306a36Sopenharmony_ci struct netdev_nested_priv *priv) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci if (netif_is_bridge_master(dev)) 52862306a36Sopenharmony_ci priv->data = dev; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return 0; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic struct net_device *prestera_get_upper_bridge_dev(struct net_device *dev) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct netdev_nested_priv priv = { }; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci netdev_walk_all_upper_dev_rcu(dev, prestera_match_upper_bridge_dev, 53862306a36Sopenharmony_ci &priv); 53962306a36Sopenharmony_ci return priv.data; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic struct prestera_bridge_port * 54362306a36Sopenharmony_ciprestera_bridge_port_by_dev(struct prestera_switchdev *swdev, 54462306a36Sopenharmony_ci struct net_device *dev) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci struct net_device *br_dev = prestera_get_upper_bridge_dev(dev); 54762306a36Sopenharmony_ci struct prestera_bridge *bridge; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (!br_dev) 55062306a36Sopenharmony_ci return NULL; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci bridge = prestera_bridge_by_dev(swdev, br_dev); 55362306a36Sopenharmony_ci if (!bridge) 55462306a36Sopenharmony_ci return NULL; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return __prestera_bridge_port_by_dev(bridge, dev); 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic struct prestera_bridge_port * 56062306a36Sopenharmony_ciprestera_bridge_port_create(struct prestera_bridge *bridge, 56162306a36Sopenharmony_ci struct net_device *dev) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci br_port = kzalloc(sizeof(*br_port), GFP_KERNEL); 56662306a36Sopenharmony_ci if (!br_port) 56762306a36Sopenharmony_ci return NULL; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci br_port->flags = BR_LEARNING | BR_FLOOD | BR_LEARNING_SYNC | 57062306a36Sopenharmony_ci BR_MCAST_FLOOD; 57162306a36Sopenharmony_ci br_port->stp_state = BR_STATE_DISABLED; 57262306a36Sopenharmony_ci refcount_set(&br_port->ref_count, 1); 57362306a36Sopenharmony_ci br_port->bridge = bridge; 57462306a36Sopenharmony_ci br_port->dev = dev; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci INIT_LIST_HEAD(&br_port->vlan_list); 57762306a36Sopenharmony_ci list_add(&br_port->head, &bridge->port_list); 57862306a36Sopenharmony_ci INIT_LIST_HEAD(&br_port->br_mdb_port_list); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return br_port; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic void 58462306a36Sopenharmony_ciprestera_bridge_port_destroy(struct prestera_bridge_port *br_port) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci list_del(&br_port->head); 58762306a36Sopenharmony_ci WARN_ON(!list_empty(&br_port->vlan_list)); 58862306a36Sopenharmony_ci WARN_ON(!list_empty(&br_port->br_mdb_port_list)); 58962306a36Sopenharmony_ci kfree(br_port); 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic void prestera_bridge_port_get(struct prestera_bridge_port *br_port) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci refcount_inc(&br_port->ref_count); 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic void prestera_bridge_port_put(struct prestera_bridge_port *br_port) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct prestera_bridge *bridge = br_port->bridge; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (refcount_dec_and_test(&br_port->ref_count)) { 60262306a36Sopenharmony_ci prestera_bridge_port_destroy(br_port); 60362306a36Sopenharmony_ci prestera_bridge_put(bridge); 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic struct prestera_bridge_port * 60862306a36Sopenharmony_ciprestera_bridge_port_add(struct prestera_bridge *bridge, struct net_device *dev) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci br_port = __prestera_bridge_port_by_dev(bridge, dev); 61362306a36Sopenharmony_ci if (br_port) { 61462306a36Sopenharmony_ci prestera_bridge_port_get(br_port); 61562306a36Sopenharmony_ci return br_port; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci br_port = prestera_bridge_port_create(bridge, dev); 61962306a36Sopenharmony_ci if (!br_port) 62062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci return br_port; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic int 62662306a36Sopenharmony_ciprestera_bridge_1d_port_join(struct prestera_bridge_port *br_port) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(br_port->dev); 62962306a36Sopenharmony_ci struct prestera_bridge *bridge = br_port->bridge; 63062306a36Sopenharmony_ci int err; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci err = prestera_hw_bridge_port_add(port, bridge->bridge_id); 63362306a36Sopenharmony_ci if (err) 63462306a36Sopenharmony_ci return err; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci err = prestera_br_port_flags_set(br_port, port); 63762306a36Sopenharmony_ci if (err) 63862306a36Sopenharmony_ci goto err_flags2port_set; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cierr_flags2port_set: 64362306a36Sopenharmony_ci prestera_hw_bridge_port_delete(port, bridge->bridge_id); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci return err; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ciint prestera_bridge_port_join(struct net_device *br_dev, 64962306a36Sopenharmony_ci struct prestera_port *port, 65062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct prestera_switchdev *swdev = port->sw->swdev; 65362306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 65462306a36Sopenharmony_ci struct prestera_bridge *bridge; 65562306a36Sopenharmony_ci int err; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci bridge = prestera_bridge_by_dev(swdev, br_dev); 65862306a36Sopenharmony_ci if (!bridge) { 65962306a36Sopenharmony_ci bridge = prestera_bridge_create(swdev, br_dev); 66062306a36Sopenharmony_ci if (IS_ERR(bridge)) 66162306a36Sopenharmony_ci return PTR_ERR(bridge); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci br_port = prestera_bridge_port_add(bridge, port->dev); 66562306a36Sopenharmony_ci if (IS_ERR(br_port)) { 66662306a36Sopenharmony_ci prestera_bridge_put(bridge); 66762306a36Sopenharmony_ci return PTR_ERR(br_port); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci err = switchdev_bridge_port_offload(br_port->dev, port->dev, NULL, 67162306a36Sopenharmony_ci NULL, NULL, false, extack); 67262306a36Sopenharmony_ci if (err) 67362306a36Sopenharmony_ci goto err_switchdev_offload; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (bridge->vlan_enabled) 67662306a36Sopenharmony_ci return 0; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci err = prestera_bridge_1d_port_join(br_port); 67962306a36Sopenharmony_ci if (err) 68062306a36Sopenharmony_ci goto err_port_join; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci return 0; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cierr_port_join: 68562306a36Sopenharmony_ci switchdev_bridge_port_unoffload(br_port->dev, NULL, NULL, NULL); 68662306a36Sopenharmony_cierr_switchdev_offload: 68762306a36Sopenharmony_ci prestera_bridge_port_put(br_port); 68862306a36Sopenharmony_ci return err; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic void prestera_bridge_1q_port_leave(struct prestera_bridge_port *br_port) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(br_port->dev); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci prestera_hw_fdb_flush_port(port, PRESTERA_FDB_FLUSH_MODE_ALL); 69662306a36Sopenharmony_ci prestera_port_pvid_set(port, PRESTERA_DEFAULT_VID); 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic void prestera_bridge_1d_port_leave(struct prestera_bridge_port *br_port) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(br_port->dev); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci prestera_hw_fdb_flush_port(port, PRESTERA_FDB_FLUSH_MODE_ALL); 70462306a36Sopenharmony_ci prestera_hw_bridge_port_delete(port, br_port->bridge->bridge_id); 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic int prestera_port_vid_stp_set(struct prestera_port *port, u16 vid, 70862306a36Sopenharmony_ci u8 state) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci u8 hw_state = state; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci switch (state) { 71362306a36Sopenharmony_ci case BR_STATE_DISABLED: 71462306a36Sopenharmony_ci hw_state = PRESTERA_STP_DISABLED; 71562306a36Sopenharmony_ci break; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci case BR_STATE_BLOCKING: 71862306a36Sopenharmony_ci case BR_STATE_LISTENING: 71962306a36Sopenharmony_ci hw_state = PRESTERA_STP_BLOCK_LISTEN; 72062306a36Sopenharmony_ci break; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci case BR_STATE_LEARNING: 72362306a36Sopenharmony_ci hw_state = PRESTERA_STP_LEARN; 72462306a36Sopenharmony_ci break; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci case BR_STATE_FORWARDING: 72762306a36Sopenharmony_ci hw_state = PRESTERA_STP_FORWARD; 72862306a36Sopenharmony_ci break; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci default: 73162306a36Sopenharmony_ci return -EINVAL; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci return prestera_hw_vlan_port_stp_set(port, vid, hw_state); 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_civoid prestera_bridge_port_leave(struct net_device *br_dev, 73862306a36Sopenharmony_ci struct prestera_port *port) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct prestera_switchdev *swdev = port->sw->swdev; 74162306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 74262306a36Sopenharmony_ci struct prestera_bridge *bridge; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci bridge = prestera_bridge_by_dev(swdev, br_dev); 74562306a36Sopenharmony_ci if (!bridge) 74662306a36Sopenharmony_ci return; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci br_port = __prestera_bridge_port_by_dev(bridge, port->dev); 74962306a36Sopenharmony_ci if (!br_port) 75062306a36Sopenharmony_ci return; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci bridge = br_port->bridge; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (bridge->vlan_enabled) 75562306a36Sopenharmony_ci prestera_bridge_1q_port_leave(br_port); 75662306a36Sopenharmony_ci else 75762306a36Sopenharmony_ci prestera_bridge_1d_port_leave(br_port); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci switchdev_bridge_port_unoffload(br_port->dev, NULL, NULL, NULL); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci prestera_mdb_flush_bridge_port(br_port); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci prestera_br_port_flags_reset(br_port, port); 76462306a36Sopenharmony_ci prestera_port_vid_stp_set(port, PRESTERA_VID_ALL, BR_STATE_FORWARDING); 76562306a36Sopenharmony_ci prestera_bridge_port_put(br_port); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic int prestera_port_attr_br_flags_set(struct prestera_port *port, 76962306a36Sopenharmony_ci struct net_device *dev, 77062306a36Sopenharmony_ci struct switchdev_brport_flags flags) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci br_port = prestera_bridge_port_by_dev(port->sw->swdev, dev); 77562306a36Sopenharmony_ci if (!br_port) 77662306a36Sopenharmony_ci return 0; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci br_port->flags &= ~flags.mask; 77962306a36Sopenharmony_ci br_port->flags |= flags.val & flags.mask; 78062306a36Sopenharmony_ci return prestera_br_port_flags_set(br_port, port); 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic int prestera_port_attr_br_ageing_set(struct prestera_port *port, 78462306a36Sopenharmony_ci unsigned long ageing_clock_t) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t); 78762306a36Sopenharmony_ci u32 ageing_time_ms = jiffies_to_msecs(ageing_jiffies); 78862306a36Sopenharmony_ci struct prestera_switch *sw = port->sw; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (ageing_time_ms < PRESTERA_MIN_AGEING_TIME_MS || 79162306a36Sopenharmony_ci ageing_time_ms > PRESTERA_MAX_AGEING_TIME_MS) 79262306a36Sopenharmony_ci return -ERANGE; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci return prestera_hw_switch_ageing_set(sw, ageing_time_ms); 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic int prestera_port_attr_br_vlan_set(struct prestera_port *port, 79862306a36Sopenharmony_ci struct net_device *dev, 79962306a36Sopenharmony_ci bool vlan_enabled) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci struct prestera_switch *sw = port->sw; 80262306a36Sopenharmony_ci struct prestera_bridge *bridge; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci bridge = prestera_bridge_by_dev(sw->swdev, dev); 80562306a36Sopenharmony_ci if (WARN_ON(!bridge)) 80662306a36Sopenharmony_ci return -EINVAL; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (bridge->vlan_enabled == vlan_enabled) 80962306a36Sopenharmony_ci return 0; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci netdev_err(bridge->dev, "VLAN filtering can't be changed for existing bridge\n"); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return -EINVAL; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic int prestera_port_bridge_vlan_stp_set(struct prestera_port *port, 81762306a36Sopenharmony_ci struct prestera_bridge_vlan *br_vlan, 81862306a36Sopenharmony_ci u8 state) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci struct prestera_port_vlan *port_vlan; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci list_for_each_entry(port_vlan, &br_vlan->port_vlan_list, br_vlan_head) { 82362306a36Sopenharmony_ci if (port_vlan->port != port) 82462306a36Sopenharmony_ci continue; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci return prestera_port_vid_stp_set(port, br_vlan->vid, state); 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci return 0; 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistatic int prestera_port_attr_stp_state_set(struct prestera_port *port, 83362306a36Sopenharmony_ci struct net_device *dev, 83462306a36Sopenharmony_ci u8 state) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 83762306a36Sopenharmony_ci struct prestera_bridge_vlan *br_vlan; 83862306a36Sopenharmony_ci int err; 83962306a36Sopenharmony_ci u16 vid; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci br_port = prestera_bridge_port_by_dev(port->sw->swdev, dev); 84262306a36Sopenharmony_ci if (!br_port) 84362306a36Sopenharmony_ci return 0; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (!br_port->bridge->vlan_enabled) { 84662306a36Sopenharmony_ci vid = br_port->bridge->bridge_id; 84762306a36Sopenharmony_ci err = prestera_port_vid_stp_set(port, vid, state); 84862306a36Sopenharmony_ci if (err) 84962306a36Sopenharmony_ci goto err_port_stp_set; 85062306a36Sopenharmony_ci } else { 85162306a36Sopenharmony_ci list_for_each_entry(br_vlan, &br_port->vlan_list, head) { 85262306a36Sopenharmony_ci err = prestera_port_bridge_vlan_stp_set(port, br_vlan, 85362306a36Sopenharmony_ci state); 85462306a36Sopenharmony_ci if (err) 85562306a36Sopenharmony_ci goto err_port_vlan_stp_set; 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci br_port->stp_state = state; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci return 0; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cierr_port_vlan_stp_set: 86462306a36Sopenharmony_ci list_for_each_entry_continue_reverse(br_vlan, &br_port->vlan_list, head) 86562306a36Sopenharmony_ci prestera_port_bridge_vlan_stp_set(port, br_vlan, br_port->stp_state); 86662306a36Sopenharmony_ci return err; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cierr_port_stp_set: 86962306a36Sopenharmony_ci prestera_port_vid_stp_set(port, vid, br_port->stp_state); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci return err; 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic int 87562306a36Sopenharmony_ciprestera_br_port_lag_mdb_mc_enable_sync(struct prestera_bridge_port *br_port, 87662306a36Sopenharmony_ci bool enabled) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci struct prestera_port *pr_port; 87962306a36Sopenharmony_ci struct prestera_switch *sw; 88062306a36Sopenharmony_ci u16 lag_id; 88162306a36Sopenharmony_ci int err; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci pr_port = prestera_port_dev_lower_find(br_port->dev); 88462306a36Sopenharmony_ci if (!pr_port) 88562306a36Sopenharmony_ci return 0; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci sw = pr_port->sw; 88862306a36Sopenharmony_ci err = prestera_lag_id(sw, br_port->dev, &lag_id); 88962306a36Sopenharmony_ci if (err) 89062306a36Sopenharmony_ci return err; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci list_for_each_entry(pr_port, &sw->port_list, list) { 89362306a36Sopenharmony_ci if (pr_port->lag->lag_id == lag_id) { 89462306a36Sopenharmony_ci err = prestera_port_mc_flood_set(pr_port, enabled); 89562306a36Sopenharmony_ci if (err) 89662306a36Sopenharmony_ci return err; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci return 0; 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic int prestera_br_mdb_mc_enable_sync(struct prestera_bridge *br_dev) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 90662306a36Sopenharmony_ci struct prestera_port *port; 90762306a36Sopenharmony_ci bool enabled; 90862306a36Sopenharmony_ci int err; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci /* if mrouter exists: 91162306a36Sopenharmony_ci * - make sure every mrouter receives unreg mcast traffic; 91262306a36Sopenharmony_ci * if mrouter doesn't exists: 91362306a36Sopenharmony_ci * - make sure every port receives unreg mcast traffic; 91462306a36Sopenharmony_ci */ 91562306a36Sopenharmony_ci list_for_each_entry(br_port, &br_dev->port_list, head) { 91662306a36Sopenharmony_ci if (br_dev->multicast_enabled && br_dev->mrouter_exist) 91762306a36Sopenharmony_ci enabled = br_port->mrouter; 91862306a36Sopenharmony_ci else 91962306a36Sopenharmony_ci enabled = br_port->flags & BR_MCAST_FLOOD; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (netif_is_lag_master(br_port->dev)) { 92262306a36Sopenharmony_ci err = prestera_br_port_lag_mdb_mc_enable_sync(br_port, 92362306a36Sopenharmony_ci enabled); 92462306a36Sopenharmony_ci if (err) 92562306a36Sopenharmony_ci return err; 92662306a36Sopenharmony_ci continue; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci port = prestera_port_dev_lower_find(br_port->dev); 93062306a36Sopenharmony_ci if (!port) 93162306a36Sopenharmony_ci continue; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci err = prestera_port_mc_flood_set(port, enabled); 93462306a36Sopenharmony_ci if (err) 93562306a36Sopenharmony_ci return err; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci return 0; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic bool 94262306a36Sopenharmony_ciprestera_br_mdb_port_is_member(struct prestera_br_mdb_entry *br_mdb, 94362306a36Sopenharmony_ci struct net_device *orig_dev) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct prestera_br_mdb_port *tmp_port; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci list_for_each_entry(tmp_port, &br_mdb->br_mdb_port_list, 94862306a36Sopenharmony_ci br_mdb_port_node) 94962306a36Sopenharmony_ci if (tmp_port->br_port->dev == orig_dev) 95062306a36Sopenharmony_ci return true; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci return false; 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic int 95662306a36Sopenharmony_ciprestera_mdb_port_add(struct prestera_mdb_entry *mdb, 95762306a36Sopenharmony_ci struct net_device *orig_dev, 95862306a36Sopenharmony_ci const unsigned char addr[ETH_ALEN], u16 vid) 95962306a36Sopenharmony_ci{ 96062306a36Sopenharmony_ci struct prestera_flood_domain *flood_domain = mdb->flood_domain; 96162306a36Sopenharmony_ci int err; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci if (!prestera_flood_domain_port_find(flood_domain, 96462306a36Sopenharmony_ci orig_dev, vid)) { 96562306a36Sopenharmony_ci err = prestera_flood_domain_port_create(flood_domain, orig_dev, 96662306a36Sopenharmony_ci vid); 96762306a36Sopenharmony_ci if (err) 96862306a36Sopenharmony_ci return err; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci return 0; 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci/* Sync bridge mdb (software table) with HW table (if MC is enabled). */ 97562306a36Sopenharmony_cistatic int prestera_br_mdb_sync(struct prestera_bridge *br_dev) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct prestera_br_mdb_port *br_mdb_port; 97862306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 97962306a36Sopenharmony_ci struct prestera_br_mdb_entry *br_mdb; 98062306a36Sopenharmony_ci struct prestera_mdb_entry *mdb; 98162306a36Sopenharmony_ci struct prestera_port *pr_port; 98262306a36Sopenharmony_ci int err = 0; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (!br_dev->multicast_enabled) 98562306a36Sopenharmony_ci return 0; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci list_for_each_entry(br_mdb, &br_dev->br_mdb_entry_list, 98862306a36Sopenharmony_ci br_mdb_entry_node) { 98962306a36Sopenharmony_ci mdb = br_mdb->mdb; 99062306a36Sopenharmony_ci /* Make sure every port that explicitly been added to the mdb 99162306a36Sopenharmony_ci * joins the specified group. 99262306a36Sopenharmony_ci */ 99362306a36Sopenharmony_ci list_for_each_entry(br_mdb_port, &br_mdb->br_mdb_port_list, 99462306a36Sopenharmony_ci br_mdb_port_node) { 99562306a36Sopenharmony_ci br_port = br_mdb_port->br_port; 99662306a36Sopenharmony_ci pr_port = prestera_port_dev_lower_find(br_port->dev); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* Match only mdb and br_mdb ports that belong to the 99962306a36Sopenharmony_ci * same broadcast domain. 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_ci if (br_dev->vlan_enabled && 100262306a36Sopenharmony_ci !prestera_port_vlan_by_vid(pr_port, 100362306a36Sopenharmony_ci mdb->vid)) 100462306a36Sopenharmony_ci continue; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci /* If port is not in MDB or there's no Mrouter 100762306a36Sopenharmony_ci * clear HW mdb. 100862306a36Sopenharmony_ci */ 100962306a36Sopenharmony_ci if (prestera_br_mdb_port_is_member(br_mdb, 101062306a36Sopenharmony_ci br_mdb_port->br_port->dev) && 101162306a36Sopenharmony_ci br_dev->mrouter_exist) 101262306a36Sopenharmony_ci err = prestera_mdb_port_add(mdb, br_port->dev, 101362306a36Sopenharmony_ci mdb->addr, 101462306a36Sopenharmony_ci mdb->vid); 101562306a36Sopenharmony_ci else 101662306a36Sopenharmony_ci prestera_mdb_port_del(mdb, br_port->dev); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci if (err) 101962306a36Sopenharmony_ci return err; 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci /* Make sure that every mrouter port joins every MC group int 102362306a36Sopenharmony_ci * broadcast domain. If it's not an mrouter - it should leave 102462306a36Sopenharmony_ci */ 102562306a36Sopenharmony_ci list_for_each_entry(br_port, &br_dev->port_list, head) { 102662306a36Sopenharmony_ci pr_port = prestera_port_dev_lower_find(br_port->dev); 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci /* Make sure mrouter woudln't receive traffci from 102962306a36Sopenharmony_ci * another broadcast domain (e.g. from a vlan, which 103062306a36Sopenharmony_ci * mrouter port is not a member of). 103162306a36Sopenharmony_ci */ 103262306a36Sopenharmony_ci if (br_dev->vlan_enabled && 103362306a36Sopenharmony_ci !prestera_port_vlan_by_vid(pr_port, 103462306a36Sopenharmony_ci mdb->vid)) 103562306a36Sopenharmony_ci continue; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (br_port->mrouter) { 103862306a36Sopenharmony_ci err = prestera_mdb_port_add(mdb, br_port->dev, 103962306a36Sopenharmony_ci mdb->addr, 104062306a36Sopenharmony_ci mdb->vid); 104162306a36Sopenharmony_ci if (err) 104262306a36Sopenharmony_ci return err; 104362306a36Sopenharmony_ci } else if (!br_port->mrouter && 104462306a36Sopenharmony_ci !prestera_br_mdb_port_is_member 104562306a36Sopenharmony_ci (br_mdb, br_port->dev)) { 104662306a36Sopenharmony_ci prestera_mdb_port_del(mdb, br_port->dev); 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci return 0; 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic int 105562306a36Sopenharmony_ciprestera_mdb_enable_set(struct prestera_br_mdb_entry *br_mdb, bool enable) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci int err; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (enable != br_mdb->enabled) { 106062306a36Sopenharmony_ci if (enable) 106162306a36Sopenharmony_ci err = prestera_hw_mdb_create(br_mdb->mdb); 106262306a36Sopenharmony_ci else 106362306a36Sopenharmony_ci err = prestera_hw_mdb_destroy(br_mdb->mdb); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci if (err) 106662306a36Sopenharmony_ci return err; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci br_mdb->enabled = enable; 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci return 0; 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_cistatic int 107562306a36Sopenharmony_ciprestera_br_mdb_enable_set(struct prestera_bridge *br_dev, bool enable) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct prestera_br_mdb_entry *br_mdb; 107862306a36Sopenharmony_ci int err; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci list_for_each_entry(br_mdb, &br_dev->br_mdb_entry_list, 108162306a36Sopenharmony_ci br_mdb_entry_node) { 108262306a36Sopenharmony_ci err = prestera_mdb_enable_set(br_mdb, enable); 108362306a36Sopenharmony_ci if (err) 108462306a36Sopenharmony_ci return err; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci return 0; 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_cistatic int prestera_port_attr_br_mc_disabled_set(struct prestera_port *port, 109162306a36Sopenharmony_ci struct net_device *orig_dev, 109262306a36Sopenharmony_ci bool mc_disabled) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci struct prestera_switch *sw = port->sw; 109562306a36Sopenharmony_ci struct prestera_bridge *br_dev; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci br_dev = prestera_bridge_find(sw, orig_dev); 109862306a36Sopenharmony_ci if (!br_dev) 109962306a36Sopenharmony_ci return 0; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci br_dev->multicast_enabled = !mc_disabled; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci /* There's no point in enabling mdb back if router is missing. */ 110462306a36Sopenharmony_ci WARN_ON(prestera_br_mdb_enable_set(br_dev, br_dev->multicast_enabled && 110562306a36Sopenharmony_ci br_dev->mrouter_exist)); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci WARN_ON(prestera_br_mdb_sync(br_dev)); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci WARN_ON(prestera_br_mdb_mc_enable_sync(br_dev)); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci return 0; 111262306a36Sopenharmony_ci} 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_cistatic bool 111562306a36Sopenharmony_ciprestera_bridge_mdb_mc_mrouter_exists(struct prestera_bridge *br_dev) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci list_for_each_entry(br_port, &br_dev->port_list, head) 112062306a36Sopenharmony_ci if (br_port->mrouter) 112162306a36Sopenharmony_ci return true; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci return false; 112462306a36Sopenharmony_ci} 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_cistatic int 112762306a36Sopenharmony_ciprestera_port_attr_mrouter_set(struct prestera_port *port, 112862306a36Sopenharmony_ci struct net_device *orig_dev, 112962306a36Sopenharmony_ci bool is_port_mrouter) 113062306a36Sopenharmony_ci{ 113162306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 113262306a36Sopenharmony_ci struct prestera_bridge *br_dev; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci br_port = prestera_bridge_port_find(port->sw, orig_dev); 113562306a36Sopenharmony_ci if (!br_port) 113662306a36Sopenharmony_ci return 0; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci br_dev = br_port->bridge; 113962306a36Sopenharmony_ci br_port->mrouter = is_port_mrouter; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci br_dev->mrouter_exist = prestera_bridge_mdb_mc_mrouter_exists(br_dev); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci /* Enable MDB processing if both mrouter exists and mc is enabled. 114462306a36Sopenharmony_ci * In case if MC enabled, but there is no mrouter, device would flood 114562306a36Sopenharmony_ci * all multicast traffic (even if MDB table is not empty) with the use 114662306a36Sopenharmony_ci * of bridge's flood capabilities (without the use of flood_domain). 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_ci WARN_ON(prestera_br_mdb_enable_set(br_dev, br_dev->multicast_enabled && 114962306a36Sopenharmony_ci br_dev->mrouter_exist)); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci WARN_ON(prestera_br_mdb_sync(br_dev)); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci WARN_ON(prestera_br_mdb_mc_enable_sync(br_dev)); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci return 0; 115662306a36Sopenharmony_ci} 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_cistatic int prestera_port_obj_attr_set(struct net_device *dev, const void *ctx, 115962306a36Sopenharmony_ci const struct switchdev_attr *attr, 116062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 116162306a36Sopenharmony_ci{ 116262306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 116362306a36Sopenharmony_ci int err = 0; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci switch (attr->id) { 116662306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_STP_STATE: 116762306a36Sopenharmony_ci err = prestera_port_attr_stp_state_set(port, attr->orig_dev, 116862306a36Sopenharmony_ci attr->u.stp_state); 116962306a36Sopenharmony_ci break; 117062306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: 117162306a36Sopenharmony_ci if (attr->u.brport_flags.mask & 117262306a36Sopenharmony_ci ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_PORT_LOCKED)) 117362306a36Sopenharmony_ci err = -EINVAL; 117462306a36Sopenharmony_ci break; 117562306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: 117662306a36Sopenharmony_ci err = prestera_port_attr_br_flags_set(port, attr->orig_dev, 117762306a36Sopenharmony_ci attr->u.brport_flags); 117862306a36Sopenharmony_ci break; 117962306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: 118062306a36Sopenharmony_ci err = prestera_port_attr_br_ageing_set(port, 118162306a36Sopenharmony_ci attr->u.ageing_time); 118262306a36Sopenharmony_ci break; 118362306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: 118462306a36Sopenharmony_ci err = prestera_port_attr_br_vlan_set(port, attr->orig_dev, 118562306a36Sopenharmony_ci attr->u.vlan_filtering); 118662306a36Sopenharmony_ci break; 118762306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_MROUTER: 118862306a36Sopenharmony_ci err = prestera_port_attr_mrouter_set(port, attr->orig_dev, 118962306a36Sopenharmony_ci attr->u.mrouter); 119062306a36Sopenharmony_ci break; 119162306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED: 119262306a36Sopenharmony_ci err = prestera_port_attr_br_mc_disabled_set(port, attr->orig_dev, 119362306a36Sopenharmony_ci attr->u.mc_disabled); 119462306a36Sopenharmony_ci break; 119562306a36Sopenharmony_ci default: 119662306a36Sopenharmony_ci err = -EOPNOTSUPP; 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci return err; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistatic void 120362306a36Sopenharmony_ciprestera_fdb_offload_notify(struct prestera_port *port, 120462306a36Sopenharmony_ci struct switchdev_notifier_fdb_info *info) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci struct switchdev_notifier_fdb_info send_info = {}; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci send_info.addr = info->addr; 120962306a36Sopenharmony_ci send_info.vid = info->vid; 121062306a36Sopenharmony_ci send_info.offloaded = true; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, port->dev, 121362306a36Sopenharmony_ci &send_info.info, NULL); 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic int prestera_port_fdb_set(struct prestera_port *port, 121762306a36Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info, 121862306a36Sopenharmony_ci bool adding) 121962306a36Sopenharmony_ci{ 122062306a36Sopenharmony_ci struct prestera_switch *sw = port->sw; 122162306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 122262306a36Sopenharmony_ci struct prestera_bridge *bridge; 122362306a36Sopenharmony_ci int err; 122462306a36Sopenharmony_ci u16 vid; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci br_port = prestera_bridge_port_by_dev(sw->swdev, port->dev); 122762306a36Sopenharmony_ci if (!br_port) 122862306a36Sopenharmony_ci return -EINVAL; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci bridge = br_port->bridge; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci if (bridge->vlan_enabled) 123362306a36Sopenharmony_ci vid = fdb_info->vid; 123462306a36Sopenharmony_ci else 123562306a36Sopenharmony_ci vid = bridge->bridge_id; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci if (adding) 123862306a36Sopenharmony_ci err = prestera_fdb_add(port, fdb_info->addr, vid, false); 123962306a36Sopenharmony_ci else 124062306a36Sopenharmony_ci err = prestera_fdb_del(port, fdb_info->addr, vid); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci return err; 124362306a36Sopenharmony_ci} 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_cistatic void prestera_fdb_event_work(struct work_struct *work) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info; 124862306a36Sopenharmony_ci struct prestera_fdb_event_work *swdev_work; 124962306a36Sopenharmony_ci struct prestera_port *port; 125062306a36Sopenharmony_ci struct net_device *dev; 125162306a36Sopenharmony_ci int err; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci swdev_work = container_of(work, struct prestera_fdb_event_work, work); 125462306a36Sopenharmony_ci dev = swdev_work->dev; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci rtnl_lock(); 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci port = prestera_port_dev_lower_find(dev); 125962306a36Sopenharmony_ci if (!port) 126062306a36Sopenharmony_ci goto out_unlock; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci switch (swdev_work->event) { 126362306a36Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 126462306a36Sopenharmony_ci fdb_info = &swdev_work->fdb_info; 126562306a36Sopenharmony_ci if (!fdb_info->added_by_user || fdb_info->is_local) 126662306a36Sopenharmony_ci break; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci err = prestera_port_fdb_set(port, fdb_info, true); 126962306a36Sopenharmony_ci if (err) 127062306a36Sopenharmony_ci break; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci prestera_fdb_offload_notify(port, fdb_info); 127362306a36Sopenharmony_ci break; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 127662306a36Sopenharmony_ci fdb_info = &swdev_work->fdb_info; 127762306a36Sopenharmony_ci prestera_port_fdb_set(port, fdb_info, false); 127862306a36Sopenharmony_ci break; 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ciout_unlock: 128262306a36Sopenharmony_ci rtnl_unlock(); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci kfree(swdev_work->fdb_info.addr); 128562306a36Sopenharmony_ci kfree(swdev_work); 128662306a36Sopenharmony_ci dev_put(dev); 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_cistatic int prestera_switchdev_event(struct notifier_block *unused, 129062306a36Sopenharmony_ci unsigned long event, void *ptr) 129162306a36Sopenharmony_ci{ 129262306a36Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 129362306a36Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info; 129462306a36Sopenharmony_ci struct switchdev_notifier_info *info = ptr; 129562306a36Sopenharmony_ci struct prestera_fdb_event_work *swdev_work; 129662306a36Sopenharmony_ci struct net_device *upper; 129762306a36Sopenharmony_ci int err; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci if (event == SWITCHDEV_PORT_ATTR_SET) { 130062306a36Sopenharmony_ci err = switchdev_handle_port_attr_set(dev, ptr, 130162306a36Sopenharmony_ci prestera_netdev_check, 130262306a36Sopenharmony_ci prestera_port_obj_attr_set); 130362306a36Sopenharmony_ci return notifier_from_errno(err); 130462306a36Sopenharmony_ci } 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci if (!prestera_netdev_check(dev)) 130762306a36Sopenharmony_ci return NOTIFY_DONE; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci upper = netdev_master_upper_dev_get_rcu(dev); 131062306a36Sopenharmony_ci if (!upper) 131162306a36Sopenharmony_ci return NOTIFY_DONE; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci if (!netif_is_bridge_master(upper)) 131462306a36Sopenharmony_ci return NOTIFY_DONE; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci swdev_work = kzalloc(sizeof(*swdev_work), GFP_ATOMIC); 131762306a36Sopenharmony_ci if (!swdev_work) 131862306a36Sopenharmony_ci return NOTIFY_BAD; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci swdev_work->event = event; 132162306a36Sopenharmony_ci swdev_work->dev = dev; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci switch (event) { 132462306a36Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 132562306a36Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 132662306a36Sopenharmony_ci fdb_info = container_of(info, 132762306a36Sopenharmony_ci struct switchdev_notifier_fdb_info, 132862306a36Sopenharmony_ci info); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci INIT_WORK(&swdev_work->work, prestera_fdb_event_work); 133162306a36Sopenharmony_ci memcpy(&swdev_work->fdb_info, ptr, 133262306a36Sopenharmony_ci sizeof(swdev_work->fdb_info)); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci swdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); 133562306a36Sopenharmony_ci if (!swdev_work->fdb_info.addr) 133662306a36Sopenharmony_ci goto out_bad; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci ether_addr_copy((u8 *)swdev_work->fdb_info.addr, 133962306a36Sopenharmony_ci fdb_info->addr); 134062306a36Sopenharmony_ci dev_hold(dev); 134162306a36Sopenharmony_ci break; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci default: 134462306a36Sopenharmony_ci kfree(swdev_work); 134562306a36Sopenharmony_ci return NOTIFY_DONE; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci queue_work(swdev_wq, &swdev_work->work); 134962306a36Sopenharmony_ci return NOTIFY_DONE; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ciout_bad: 135262306a36Sopenharmony_ci kfree(swdev_work); 135362306a36Sopenharmony_ci return NOTIFY_BAD; 135462306a36Sopenharmony_ci} 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_cistatic int 135762306a36Sopenharmony_ciprestera_port_vlan_bridge_join(struct prestera_port_vlan *port_vlan, 135862306a36Sopenharmony_ci struct prestera_bridge_port *br_port) 135962306a36Sopenharmony_ci{ 136062306a36Sopenharmony_ci struct prestera_port *port = port_vlan->port; 136162306a36Sopenharmony_ci struct prestera_bridge_vlan *br_vlan; 136262306a36Sopenharmony_ci u16 vid = port_vlan->vid; 136362306a36Sopenharmony_ci int err; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci if (port_vlan->br_port) 136662306a36Sopenharmony_ci return 0; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci err = prestera_br_port_flags_set(br_port, port); 136962306a36Sopenharmony_ci if (err) 137062306a36Sopenharmony_ci goto err_flags2port_set; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci err = prestera_port_vid_stp_set(port, vid, br_port->stp_state); 137362306a36Sopenharmony_ci if (err) 137462306a36Sopenharmony_ci goto err_port_vid_stp_set; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci br_vlan = prestera_bridge_vlan_by_vid(br_port, vid); 137762306a36Sopenharmony_ci if (!br_vlan) { 137862306a36Sopenharmony_ci br_vlan = prestera_bridge_vlan_create(br_port, vid); 137962306a36Sopenharmony_ci if (!br_vlan) { 138062306a36Sopenharmony_ci err = -ENOMEM; 138162306a36Sopenharmony_ci goto err_bridge_vlan_get; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci list_add(&port_vlan->br_vlan_head, &br_vlan->port_vlan_list); 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci prestera_bridge_port_get(br_port); 138862306a36Sopenharmony_ci port_vlan->br_port = br_port; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci return 0; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_cierr_bridge_vlan_get: 139362306a36Sopenharmony_ci prestera_port_vid_stp_set(port, vid, BR_STATE_FORWARDING); 139462306a36Sopenharmony_cierr_port_vid_stp_set: 139562306a36Sopenharmony_ci prestera_br_port_flags_reset(br_port, port); 139662306a36Sopenharmony_cierr_flags2port_set: 139762306a36Sopenharmony_ci return err; 139862306a36Sopenharmony_ci} 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cistatic int 140162306a36Sopenharmony_ciprestera_bridge_port_vlan_add(struct prestera_port *port, 140262306a36Sopenharmony_ci struct prestera_bridge_port *br_port, 140362306a36Sopenharmony_ci u16 vid, bool is_untagged, bool is_pvid, 140462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 140562306a36Sopenharmony_ci{ 140662306a36Sopenharmony_ci struct prestera_port_vlan *port_vlan; 140762306a36Sopenharmony_ci u16 old_pvid = port->pvid; 140862306a36Sopenharmony_ci u16 pvid; 140962306a36Sopenharmony_ci int err; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci if (is_pvid) 141262306a36Sopenharmony_ci pvid = vid; 141362306a36Sopenharmony_ci else 141462306a36Sopenharmony_ci pvid = port->pvid == vid ? 0 : port->pvid; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci port_vlan = prestera_port_vlan_by_vid(port, vid); 141762306a36Sopenharmony_ci if (port_vlan && port_vlan->br_port != br_port) 141862306a36Sopenharmony_ci return -EEXIST; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci if (!port_vlan) { 142162306a36Sopenharmony_ci port_vlan = prestera_port_vlan_create(port, vid, is_untagged); 142262306a36Sopenharmony_ci if (IS_ERR(port_vlan)) 142362306a36Sopenharmony_ci return PTR_ERR(port_vlan); 142462306a36Sopenharmony_ci } else { 142562306a36Sopenharmony_ci err = prestera_hw_vlan_port_set(port, vid, true, is_untagged); 142662306a36Sopenharmony_ci if (err) 142762306a36Sopenharmony_ci goto err_port_vlan_set; 142862306a36Sopenharmony_ci } 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci err = prestera_port_pvid_set(port, pvid); 143162306a36Sopenharmony_ci if (err) 143262306a36Sopenharmony_ci goto err_port_pvid_set; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci err = prestera_port_vlan_bridge_join(port_vlan, br_port); 143562306a36Sopenharmony_ci if (err) 143662306a36Sopenharmony_ci goto err_port_vlan_bridge_join; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci return 0; 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_cierr_port_vlan_bridge_join: 144162306a36Sopenharmony_ci prestera_port_pvid_set(port, old_pvid); 144262306a36Sopenharmony_cierr_port_pvid_set: 144362306a36Sopenharmony_ci prestera_hw_vlan_port_set(port, vid, false, false); 144462306a36Sopenharmony_cierr_port_vlan_set: 144562306a36Sopenharmony_ci prestera_port_vlan_destroy(port_vlan); 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci return err; 144862306a36Sopenharmony_ci} 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_cistatic void 145162306a36Sopenharmony_ciprestera_bridge_port_vlan_del(struct prestera_port *port, 145262306a36Sopenharmony_ci struct prestera_bridge_port *br_port, u16 vid) 145362306a36Sopenharmony_ci{ 145462306a36Sopenharmony_ci u16 pvid = port->pvid == vid ? 0 : port->pvid; 145562306a36Sopenharmony_ci struct prestera_port_vlan *port_vlan; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci port_vlan = prestera_port_vlan_by_vid(port, vid); 145862306a36Sopenharmony_ci if (WARN_ON(!port_vlan)) 145962306a36Sopenharmony_ci return; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci prestera_port_vlan_bridge_leave(port_vlan); 146262306a36Sopenharmony_ci prestera_port_pvid_set(port, pvid); 146362306a36Sopenharmony_ci prestera_port_vlan_destroy(port_vlan); 146462306a36Sopenharmony_ci} 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_cistatic int prestera_port_vlans_add(struct prestera_port *port, 146762306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan, 146862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 146962306a36Sopenharmony_ci{ 147062306a36Sopenharmony_ci bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 147162306a36Sopenharmony_ci bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; 147262306a36Sopenharmony_ci struct net_device *orig_dev = vlan->obj.orig_dev; 147362306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 147462306a36Sopenharmony_ci struct prestera_switch *sw = port->sw; 147562306a36Sopenharmony_ci struct prestera_bridge *bridge; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci if (netif_is_bridge_master(orig_dev)) 147862306a36Sopenharmony_ci return 0; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci br_port = prestera_bridge_port_by_dev(sw->swdev, port->dev); 148162306a36Sopenharmony_ci if (WARN_ON(!br_port)) 148262306a36Sopenharmony_ci return -EINVAL; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci bridge = br_port->bridge; 148562306a36Sopenharmony_ci if (!bridge->vlan_enabled) 148662306a36Sopenharmony_ci return 0; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci return prestera_bridge_port_vlan_add(port, br_port, 148962306a36Sopenharmony_ci vlan->vid, flag_untagged, 149062306a36Sopenharmony_ci flag_pvid, extack); 149162306a36Sopenharmony_ci} 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_cistatic struct prestera_br_mdb_entry * 149462306a36Sopenharmony_ciprestera_br_mdb_entry_create(struct prestera_switch *sw, 149562306a36Sopenharmony_ci struct prestera_bridge *br_dev, 149662306a36Sopenharmony_ci const unsigned char *addr, u16 vid) 149762306a36Sopenharmony_ci{ 149862306a36Sopenharmony_ci struct prestera_br_mdb_entry *br_mdb_entry; 149962306a36Sopenharmony_ci struct prestera_mdb_entry *mdb_entry; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci br_mdb_entry = kzalloc(sizeof(*br_mdb_entry), GFP_KERNEL); 150262306a36Sopenharmony_ci if (!br_mdb_entry) 150362306a36Sopenharmony_ci return NULL; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci mdb_entry = prestera_mdb_entry_create(sw, addr, vid); 150662306a36Sopenharmony_ci if (!mdb_entry) 150762306a36Sopenharmony_ci goto err_mdb_alloc; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci br_mdb_entry->mdb = mdb_entry; 151062306a36Sopenharmony_ci br_mdb_entry->bridge = br_dev; 151162306a36Sopenharmony_ci br_mdb_entry->enabled = true; 151262306a36Sopenharmony_ci INIT_LIST_HEAD(&br_mdb_entry->br_mdb_port_list); 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci list_add(&br_mdb_entry->br_mdb_entry_node, &br_dev->br_mdb_entry_list); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci return br_mdb_entry; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_cierr_mdb_alloc: 151962306a36Sopenharmony_ci kfree(br_mdb_entry); 152062306a36Sopenharmony_ci return NULL; 152162306a36Sopenharmony_ci} 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_cistatic int prestera_br_mdb_port_add(struct prestera_br_mdb_entry *br_mdb, 152462306a36Sopenharmony_ci struct prestera_bridge_port *br_port) 152562306a36Sopenharmony_ci{ 152662306a36Sopenharmony_ci struct prestera_br_mdb_port *br_mdb_port; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci list_for_each_entry(br_mdb_port, &br_mdb->br_mdb_port_list, 152962306a36Sopenharmony_ci br_mdb_port_node) 153062306a36Sopenharmony_ci if (br_mdb_port->br_port == br_port) 153162306a36Sopenharmony_ci return 0; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci br_mdb_port = kzalloc(sizeof(*br_mdb_port), GFP_KERNEL); 153462306a36Sopenharmony_ci if (!br_mdb_port) 153562306a36Sopenharmony_ci return -ENOMEM; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci br_mdb_port->br_port = br_port; 153862306a36Sopenharmony_ci list_add(&br_mdb_port->br_mdb_port_node, 153962306a36Sopenharmony_ci &br_mdb->br_mdb_port_list); 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci return 0; 154262306a36Sopenharmony_ci} 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_cistatic struct prestera_br_mdb_entry * 154562306a36Sopenharmony_ciprestera_br_mdb_entry_find(struct prestera_bridge *br_dev, 154662306a36Sopenharmony_ci const unsigned char *addr, u16 vid) 154762306a36Sopenharmony_ci{ 154862306a36Sopenharmony_ci struct prestera_br_mdb_entry *br_mdb; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci list_for_each_entry(br_mdb, &br_dev->br_mdb_entry_list, 155162306a36Sopenharmony_ci br_mdb_entry_node) 155262306a36Sopenharmony_ci if (ether_addr_equal(&br_mdb->mdb->addr[0], addr) && 155362306a36Sopenharmony_ci vid == br_mdb->mdb->vid) 155462306a36Sopenharmony_ci return br_mdb; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci return NULL; 155762306a36Sopenharmony_ci} 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_cistatic struct prestera_br_mdb_entry * 156062306a36Sopenharmony_ciprestera_br_mdb_entry_get(struct prestera_switch *sw, 156162306a36Sopenharmony_ci struct prestera_bridge *br_dev, 156262306a36Sopenharmony_ci const unsigned char *addr, u16 vid) 156362306a36Sopenharmony_ci{ 156462306a36Sopenharmony_ci struct prestera_br_mdb_entry *br_mdb; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci br_mdb = prestera_br_mdb_entry_find(br_dev, addr, vid); 156762306a36Sopenharmony_ci if (br_mdb) 156862306a36Sopenharmony_ci return br_mdb; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci return prestera_br_mdb_entry_create(sw, br_dev, addr, vid); 157162306a36Sopenharmony_ci} 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_cistatic int 157462306a36Sopenharmony_ciprestera_mdb_port_addr_obj_add(const struct switchdev_obj_port_mdb *mdb) 157562306a36Sopenharmony_ci{ 157662306a36Sopenharmony_ci struct prestera_br_mdb_entry *br_mdb; 157762306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 157862306a36Sopenharmony_ci struct prestera_bridge *br_dev; 157962306a36Sopenharmony_ci struct prestera_switch *sw; 158062306a36Sopenharmony_ci struct prestera_port *port; 158162306a36Sopenharmony_ci int err; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci sw = prestera_switch_get(mdb->obj.orig_dev); 158462306a36Sopenharmony_ci port = prestera_port_dev_lower_find(mdb->obj.orig_dev); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci br_port = prestera_bridge_port_find(sw, mdb->obj.orig_dev); 158762306a36Sopenharmony_ci if (!br_port) 158862306a36Sopenharmony_ci return 0; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci br_dev = br_port->bridge; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci if (mdb->vid && !prestera_port_vlan_by_vid(port, mdb->vid)) 159362306a36Sopenharmony_ci return 0; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci if (mdb->vid) 159662306a36Sopenharmony_ci br_mdb = prestera_br_mdb_entry_get(sw, br_dev, &mdb->addr[0], 159762306a36Sopenharmony_ci mdb->vid); 159862306a36Sopenharmony_ci else 159962306a36Sopenharmony_ci br_mdb = prestera_br_mdb_entry_get(sw, br_dev, &mdb->addr[0], 160062306a36Sopenharmony_ci br_dev->bridge_id); 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci if (!br_mdb) 160362306a36Sopenharmony_ci return -ENOMEM; 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci /* Make sure newly allocated MDB entry gets disabled if either MC is 160662306a36Sopenharmony_ci * disabled, or the mrouter does not exist. 160762306a36Sopenharmony_ci */ 160862306a36Sopenharmony_ci WARN_ON(prestera_mdb_enable_set(br_mdb, br_dev->multicast_enabled && 160962306a36Sopenharmony_ci br_dev->mrouter_exist)); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci err = prestera_br_mdb_port_add(br_mdb, br_port); 161262306a36Sopenharmony_ci if (err) { 161362306a36Sopenharmony_ci prestera_br_mdb_entry_put(br_mdb); 161462306a36Sopenharmony_ci return err; 161562306a36Sopenharmony_ci } 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci err = prestera_br_mdb_sync(br_dev); 161862306a36Sopenharmony_ci if (err) 161962306a36Sopenharmony_ci return err; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci return 0; 162262306a36Sopenharmony_ci} 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_cistatic int prestera_port_obj_add(struct net_device *dev, const void *ctx, 162562306a36Sopenharmony_ci const struct switchdev_obj *obj, 162662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 162762306a36Sopenharmony_ci{ 162862306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 162962306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan; 163062306a36Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb; 163162306a36Sopenharmony_ci int err = 0; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci switch (obj->id) { 163462306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 163562306a36Sopenharmony_ci vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); 163662306a36Sopenharmony_ci return prestera_port_vlans_add(port, vlan, extack); 163762306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_MDB: 163862306a36Sopenharmony_ci mdb = SWITCHDEV_OBJ_PORT_MDB(obj); 163962306a36Sopenharmony_ci err = prestera_mdb_port_addr_obj_add(mdb); 164062306a36Sopenharmony_ci break; 164162306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_HOST_MDB: 164262306a36Sopenharmony_ci fallthrough; 164362306a36Sopenharmony_ci default: 164462306a36Sopenharmony_ci err = -EOPNOTSUPP; 164562306a36Sopenharmony_ci break; 164662306a36Sopenharmony_ci } 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci return err; 164962306a36Sopenharmony_ci} 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_cistatic int prestera_port_vlans_del(struct prestera_port *port, 165262306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 165362306a36Sopenharmony_ci{ 165462306a36Sopenharmony_ci struct net_device *orig_dev = vlan->obj.orig_dev; 165562306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 165662306a36Sopenharmony_ci struct prestera_switch *sw = port->sw; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci if (netif_is_bridge_master(orig_dev)) 165962306a36Sopenharmony_ci return -EOPNOTSUPP; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci br_port = prestera_bridge_port_by_dev(sw->swdev, port->dev); 166262306a36Sopenharmony_ci if (WARN_ON(!br_port)) 166362306a36Sopenharmony_ci return -EINVAL; 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci if (!br_port->bridge->vlan_enabled) 166662306a36Sopenharmony_ci return 0; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci prestera_bridge_port_vlan_del(port, br_port, vlan->vid); 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci return 0; 167162306a36Sopenharmony_ci} 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_cistatic int 167462306a36Sopenharmony_ciprestera_mdb_port_addr_obj_del(struct prestera_port *port, 167562306a36Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb) 167662306a36Sopenharmony_ci{ 167762306a36Sopenharmony_ci struct prestera_br_mdb_entry *br_mdb; 167862306a36Sopenharmony_ci struct prestera_bridge_port *br_port; 167962306a36Sopenharmony_ci struct prestera_bridge *br_dev; 168062306a36Sopenharmony_ci int err; 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci /* Bridge port no longer exists - and so does this MDB entry */ 168362306a36Sopenharmony_ci br_port = prestera_bridge_port_find(port->sw, mdb->obj.orig_dev); 168462306a36Sopenharmony_ci if (!br_port) 168562306a36Sopenharmony_ci return 0; 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci /* Removing MDB with non-existing VLAN - not supported; */ 168862306a36Sopenharmony_ci if (mdb->vid && !prestera_port_vlan_by_vid(port, mdb->vid)) 168962306a36Sopenharmony_ci return 0; 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci br_dev = br_port->bridge; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci if (br_port->bridge->vlan_enabled) 169462306a36Sopenharmony_ci br_mdb = prestera_br_mdb_entry_find(br_dev, &mdb->addr[0], 169562306a36Sopenharmony_ci mdb->vid); 169662306a36Sopenharmony_ci else 169762306a36Sopenharmony_ci br_mdb = prestera_br_mdb_entry_find(br_dev, &mdb->addr[0], 169862306a36Sopenharmony_ci br_port->bridge->bridge_id); 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci if (!br_mdb) 170162306a36Sopenharmony_ci return 0; 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci /* Since there might be a situation that this port was the last in the 170462306a36Sopenharmony_ci * MDB group, we have to both remove this port from software and HW MDB, 170562306a36Sopenharmony_ci * sync MDB table, and then destroy software MDB (if needed). 170662306a36Sopenharmony_ci */ 170762306a36Sopenharmony_ci prestera_br_mdb_port_del(br_mdb, br_port); 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci prestera_br_mdb_entry_put(br_mdb); 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci err = prestera_br_mdb_sync(br_dev); 171262306a36Sopenharmony_ci if (err) 171362306a36Sopenharmony_ci return err; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci return 0; 171662306a36Sopenharmony_ci} 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_cistatic int prestera_port_obj_del(struct net_device *dev, const void *ctx, 171962306a36Sopenharmony_ci const struct switchdev_obj *obj) 172062306a36Sopenharmony_ci{ 172162306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 172262306a36Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb; 172362306a36Sopenharmony_ci int err = 0; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci switch (obj->id) { 172662306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 172762306a36Sopenharmony_ci return prestera_port_vlans_del(port, SWITCHDEV_OBJ_PORT_VLAN(obj)); 172862306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_MDB: 172962306a36Sopenharmony_ci mdb = SWITCHDEV_OBJ_PORT_MDB(obj); 173062306a36Sopenharmony_ci err = prestera_mdb_port_addr_obj_del(port, mdb); 173162306a36Sopenharmony_ci break; 173262306a36Sopenharmony_ci default: 173362306a36Sopenharmony_ci err = -EOPNOTSUPP; 173462306a36Sopenharmony_ci break; 173562306a36Sopenharmony_ci } 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci return err; 173862306a36Sopenharmony_ci} 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_cistatic int prestera_switchdev_blk_event(struct notifier_block *unused, 174162306a36Sopenharmony_ci unsigned long event, void *ptr) 174262306a36Sopenharmony_ci{ 174362306a36Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 174462306a36Sopenharmony_ci int err; 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci switch (event) { 174762306a36Sopenharmony_ci case SWITCHDEV_PORT_OBJ_ADD: 174862306a36Sopenharmony_ci err = switchdev_handle_port_obj_add(dev, ptr, 174962306a36Sopenharmony_ci prestera_netdev_check, 175062306a36Sopenharmony_ci prestera_port_obj_add); 175162306a36Sopenharmony_ci break; 175262306a36Sopenharmony_ci case SWITCHDEV_PORT_OBJ_DEL: 175362306a36Sopenharmony_ci err = switchdev_handle_port_obj_del(dev, ptr, 175462306a36Sopenharmony_ci prestera_netdev_check, 175562306a36Sopenharmony_ci prestera_port_obj_del); 175662306a36Sopenharmony_ci break; 175762306a36Sopenharmony_ci case SWITCHDEV_PORT_ATTR_SET: 175862306a36Sopenharmony_ci err = switchdev_handle_port_attr_set(dev, ptr, 175962306a36Sopenharmony_ci prestera_netdev_check, 176062306a36Sopenharmony_ci prestera_port_obj_attr_set); 176162306a36Sopenharmony_ci break; 176262306a36Sopenharmony_ci default: 176362306a36Sopenharmony_ci return NOTIFY_DONE; 176462306a36Sopenharmony_ci } 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci return notifier_from_errno(err); 176762306a36Sopenharmony_ci} 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_cistatic void prestera_fdb_event(struct prestera_switch *sw, 177062306a36Sopenharmony_ci struct prestera_event *evt, void *arg) 177162306a36Sopenharmony_ci{ 177262306a36Sopenharmony_ci struct switchdev_notifier_fdb_info info = {}; 177362306a36Sopenharmony_ci struct net_device *dev = NULL; 177462306a36Sopenharmony_ci struct prestera_port *port; 177562306a36Sopenharmony_ci struct prestera_lag *lag; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci switch (evt->fdb_evt.type) { 177862306a36Sopenharmony_ci case PRESTERA_FDB_ENTRY_TYPE_REG_PORT: 177962306a36Sopenharmony_ci port = prestera_find_port(sw, evt->fdb_evt.dest.port_id); 178062306a36Sopenharmony_ci if (port) 178162306a36Sopenharmony_ci dev = port->dev; 178262306a36Sopenharmony_ci break; 178362306a36Sopenharmony_ci case PRESTERA_FDB_ENTRY_TYPE_LAG: 178462306a36Sopenharmony_ci lag = prestera_lag_by_id(sw, evt->fdb_evt.dest.lag_id); 178562306a36Sopenharmony_ci if (lag) 178662306a36Sopenharmony_ci dev = lag->dev; 178762306a36Sopenharmony_ci break; 178862306a36Sopenharmony_ci default: 178962306a36Sopenharmony_ci return; 179062306a36Sopenharmony_ci } 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci if (!dev) 179362306a36Sopenharmony_ci return; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci info.addr = evt->fdb_evt.data.mac; 179662306a36Sopenharmony_ci info.vid = evt->fdb_evt.vid; 179762306a36Sopenharmony_ci info.offloaded = true; 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci rtnl_lock(); 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci switch (evt->id) { 180262306a36Sopenharmony_ci case PRESTERA_FDB_EVENT_LEARNED: 180362306a36Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, 180462306a36Sopenharmony_ci dev, &info.info, NULL); 180562306a36Sopenharmony_ci break; 180662306a36Sopenharmony_ci case PRESTERA_FDB_EVENT_AGED: 180762306a36Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, 180862306a36Sopenharmony_ci dev, &info.info, NULL); 180962306a36Sopenharmony_ci break; 181062306a36Sopenharmony_ci } 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci rtnl_unlock(); 181362306a36Sopenharmony_ci} 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_cistatic int prestera_fdb_init(struct prestera_switch *sw) 181662306a36Sopenharmony_ci{ 181762306a36Sopenharmony_ci int err; 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci err = prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_FDB, 182062306a36Sopenharmony_ci prestera_fdb_event, NULL); 182162306a36Sopenharmony_ci if (err) 182262306a36Sopenharmony_ci return err; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci err = prestera_hw_switch_ageing_set(sw, PRESTERA_DEFAULT_AGEING_TIME_MS); 182562306a36Sopenharmony_ci if (err) 182662306a36Sopenharmony_ci goto err_ageing_set; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci return 0; 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_cierr_ageing_set: 183162306a36Sopenharmony_ci prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_FDB, 183262306a36Sopenharmony_ci prestera_fdb_event); 183362306a36Sopenharmony_ci return err; 183462306a36Sopenharmony_ci} 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_cistatic void prestera_fdb_fini(struct prestera_switch *sw) 183762306a36Sopenharmony_ci{ 183862306a36Sopenharmony_ci prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_FDB, 183962306a36Sopenharmony_ci prestera_fdb_event); 184062306a36Sopenharmony_ci} 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_cistatic int prestera_switchdev_handler_init(struct prestera_switchdev *swdev) 184362306a36Sopenharmony_ci{ 184462306a36Sopenharmony_ci int err; 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci swdev->swdev_nb.notifier_call = prestera_switchdev_event; 184762306a36Sopenharmony_ci err = register_switchdev_notifier(&swdev->swdev_nb); 184862306a36Sopenharmony_ci if (err) 184962306a36Sopenharmony_ci goto err_register_swdev_notifier; 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci swdev->swdev_nb_blk.notifier_call = prestera_switchdev_blk_event; 185262306a36Sopenharmony_ci err = register_switchdev_blocking_notifier(&swdev->swdev_nb_blk); 185362306a36Sopenharmony_ci if (err) 185462306a36Sopenharmony_ci goto err_register_blk_swdev_notifier; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci return 0; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_cierr_register_blk_swdev_notifier: 185962306a36Sopenharmony_ci unregister_switchdev_notifier(&swdev->swdev_nb); 186062306a36Sopenharmony_cierr_register_swdev_notifier: 186162306a36Sopenharmony_ci destroy_workqueue(swdev_wq); 186262306a36Sopenharmony_ci return err; 186362306a36Sopenharmony_ci} 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_cistatic void prestera_switchdev_handler_fini(struct prestera_switchdev *swdev) 186662306a36Sopenharmony_ci{ 186762306a36Sopenharmony_ci unregister_switchdev_blocking_notifier(&swdev->swdev_nb_blk); 186862306a36Sopenharmony_ci unregister_switchdev_notifier(&swdev->swdev_nb); 186962306a36Sopenharmony_ci} 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ciint prestera_switchdev_init(struct prestera_switch *sw) 187262306a36Sopenharmony_ci{ 187362306a36Sopenharmony_ci struct prestera_switchdev *swdev; 187462306a36Sopenharmony_ci int err; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci swdev = kzalloc(sizeof(*swdev), GFP_KERNEL); 187762306a36Sopenharmony_ci if (!swdev) 187862306a36Sopenharmony_ci return -ENOMEM; 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci sw->swdev = swdev; 188162306a36Sopenharmony_ci swdev->sw = sw; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci INIT_LIST_HEAD(&swdev->bridge_list); 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci swdev_wq = alloc_ordered_workqueue("%s_ordered", 0, "prestera_br"); 188662306a36Sopenharmony_ci if (!swdev_wq) { 188762306a36Sopenharmony_ci err = -ENOMEM; 188862306a36Sopenharmony_ci goto err_alloc_wq; 188962306a36Sopenharmony_ci } 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci err = prestera_switchdev_handler_init(swdev); 189262306a36Sopenharmony_ci if (err) 189362306a36Sopenharmony_ci goto err_swdev_init; 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci err = prestera_fdb_init(sw); 189662306a36Sopenharmony_ci if (err) 189762306a36Sopenharmony_ci goto err_fdb_init; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci return 0; 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_cierr_fdb_init: 190262306a36Sopenharmony_cierr_swdev_init: 190362306a36Sopenharmony_ci destroy_workqueue(swdev_wq); 190462306a36Sopenharmony_cierr_alloc_wq: 190562306a36Sopenharmony_ci kfree(swdev); 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci return err; 190862306a36Sopenharmony_ci} 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_civoid prestera_switchdev_fini(struct prestera_switch *sw) 191162306a36Sopenharmony_ci{ 191262306a36Sopenharmony_ci struct prestera_switchdev *swdev = sw->swdev; 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci prestera_fdb_fini(sw); 191562306a36Sopenharmony_ci prestera_switchdev_handler_fini(swdev); 191662306a36Sopenharmony_ci destroy_workqueue(swdev_wq); 191762306a36Sopenharmony_ci kfree(swdev); 191862306a36Sopenharmony_ci} 1919