18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/if_bridge.h> 58c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 68c2ecf20Sopenharmony_ci#include <linux/kernel.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/notifier.h> 98c2ecf20Sopenharmony_ci#include <net/netevent.h> 108c2ecf20Sopenharmony_ci#include <net/switchdev.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "prestera.h" 138c2ecf20Sopenharmony_ci#include "prestera_hw.h" 148c2ecf20Sopenharmony_ci#include "prestera_switchdev.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define PRESTERA_VID_ALL (0xffff) 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define PRESTERA_DEFAULT_AGEING_TIME_MS 300000 198c2ecf20Sopenharmony_ci#define PRESTERA_MAX_AGEING_TIME_MS 1000000000 208c2ecf20Sopenharmony_ci#define PRESTERA_MIN_AGEING_TIME_MS 32000 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct prestera_fdb_event_work { 238c2ecf20Sopenharmony_ci struct work_struct work; 248c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info fdb_info; 258c2ecf20Sopenharmony_ci struct net_device *dev; 268c2ecf20Sopenharmony_ci unsigned long event; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct prestera_switchdev { 308c2ecf20Sopenharmony_ci struct prestera_switch *sw; 318c2ecf20Sopenharmony_ci struct list_head bridge_list; 328c2ecf20Sopenharmony_ci bool bridge_8021q_exists; 338c2ecf20Sopenharmony_ci struct notifier_block swdev_nb_blk; 348c2ecf20Sopenharmony_ci struct notifier_block swdev_nb; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct prestera_bridge { 388c2ecf20Sopenharmony_ci struct list_head head; 398c2ecf20Sopenharmony_ci struct net_device *dev; 408c2ecf20Sopenharmony_ci struct prestera_switchdev *swdev; 418c2ecf20Sopenharmony_ci struct list_head port_list; 428c2ecf20Sopenharmony_ci bool vlan_enabled; 438c2ecf20Sopenharmony_ci u16 bridge_id; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct prestera_bridge_port { 478c2ecf20Sopenharmony_ci struct list_head head; 488c2ecf20Sopenharmony_ci struct net_device *dev; 498c2ecf20Sopenharmony_ci struct prestera_bridge *bridge; 508c2ecf20Sopenharmony_ci struct list_head vlan_list; 518c2ecf20Sopenharmony_ci refcount_t ref_count; 528c2ecf20Sopenharmony_ci unsigned long flags; 538c2ecf20Sopenharmony_ci u8 stp_state; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct prestera_bridge_vlan { 578c2ecf20Sopenharmony_ci struct list_head head; 588c2ecf20Sopenharmony_ci struct list_head port_vlan_list; 598c2ecf20Sopenharmony_ci u16 vid; 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistruct prestera_port_vlan { 638c2ecf20Sopenharmony_ci struct list_head br_vlan_head; 648c2ecf20Sopenharmony_ci struct list_head port_head; 658c2ecf20Sopenharmony_ci struct prestera_port *port; 668c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port; 678c2ecf20Sopenharmony_ci u16 vid; 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic struct workqueue_struct *swdev_wq; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void prestera_bridge_port_put(struct prestera_bridge_port *br_port); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int prestera_port_vid_stp_set(struct prestera_port *port, u16 vid, 758c2ecf20Sopenharmony_ci u8 state); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic struct prestera_bridge_vlan * 788c2ecf20Sopenharmony_ciprestera_bridge_vlan_create(struct prestera_bridge_port *br_port, u16 vid) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct prestera_bridge_vlan *br_vlan; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci br_vlan = kzalloc(sizeof(*br_vlan), GFP_KERNEL); 838c2ecf20Sopenharmony_ci if (!br_vlan) 848c2ecf20Sopenharmony_ci return NULL; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&br_vlan->port_vlan_list); 878c2ecf20Sopenharmony_ci br_vlan->vid = vid; 888c2ecf20Sopenharmony_ci list_add(&br_vlan->head, &br_port->vlan_list); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return br_vlan; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void prestera_bridge_vlan_destroy(struct prestera_bridge_vlan *br_vlan) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci list_del(&br_vlan->head); 968c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&br_vlan->port_vlan_list)); 978c2ecf20Sopenharmony_ci kfree(br_vlan); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic struct prestera_bridge_vlan * 1018c2ecf20Sopenharmony_ciprestera_bridge_vlan_by_vid(struct prestera_bridge_port *br_port, u16 vid) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct prestera_bridge_vlan *br_vlan; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci list_for_each_entry(br_vlan, &br_port->vlan_list, head) { 1068c2ecf20Sopenharmony_ci if (br_vlan->vid == vid) 1078c2ecf20Sopenharmony_ci return br_vlan; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return NULL; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int prestera_bridge_vlan_port_count(struct prestera_bridge *bridge, 1148c2ecf20Sopenharmony_ci u16 vid) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port; 1178c2ecf20Sopenharmony_ci struct prestera_bridge_vlan *br_vlan; 1188c2ecf20Sopenharmony_ci int count = 0; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci list_for_each_entry(br_port, &bridge->port_list, head) { 1218c2ecf20Sopenharmony_ci list_for_each_entry(br_vlan, &br_port->vlan_list, head) { 1228c2ecf20Sopenharmony_ci if (br_vlan->vid == vid) { 1238c2ecf20Sopenharmony_ci count += 1; 1248c2ecf20Sopenharmony_ci break; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return count; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void prestera_bridge_vlan_put(struct prestera_bridge_vlan *br_vlan) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci if (list_empty(&br_vlan->port_vlan_list)) 1358c2ecf20Sopenharmony_ci prestera_bridge_vlan_destroy(br_vlan); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic struct prestera_port_vlan * 1398c2ecf20Sopenharmony_ciprestera_port_vlan_by_vid(struct prestera_port *port, u16 vid) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct prestera_port_vlan *port_vlan; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci list_for_each_entry(port_vlan, &port->vlans_list, port_head) { 1448c2ecf20Sopenharmony_ci if (port_vlan->vid == vid) 1458c2ecf20Sopenharmony_ci return port_vlan; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return NULL; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic struct prestera_port_vlan * 1528c2ecf20Sopenharmony_ciprestera_port_vlan_create(struct prestera_port *port, u16 vid, bool untagged) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct prestera_port_vlan *port_vlan; 1558c2ecf20Sopenharmony_ci int err; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci port_vlan = prestera_port_vlan_by_vid(port, vid); 1588c2ecf20Sopenharmony_ci if (port_vlan) 1598c2ecf20Sopenharmony_ci return ERR_PTR(-EEXIST); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci err = prestera_hw_vlan_port_set(port, vid, true, untagged); 1628c2ecf20Sopenharmony_ci if (err) 1638c2ecf20Sopenharmony_ci return ERR_PTR(err); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci port_vlan = kzalloc(sizeof(*port_vlan), GFP_KERNEL); 1668c2ecf20Sopenharmony_ci if (!port_vlan) { 1678c2ecf20Sopenharmony_ci err = -ENOMEM; 1688c2ecf20Sopenharmony_ci goto err_port_vlan_alloc; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci port_vlan->port = port; 1728c2ecf20Sopenharmony_ci port_vlan->vid = vid; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci list_add(&port_vlan->port_head, &port->vlans_list); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return port_vlan; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cierr_port_vlan_alloc: 1798c2ecf20Sopenharmony_ci prestera_hw_vlan_port_set(port, vid, false, false); 1808c2ecf20Sopenharmony_ci return ERR_PTR(err); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void 1848c2ecf20Sopenharmony_ciprestera_port_vlan_bridge_leave(struct prestera_port_vlan *port_vlan) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci u32 fdb_flush_mode = PRESTERA_FDB_FLUSH_MODE_DYNAMIC; 1878c2ecf20Sopenharmony_ci struct prestera_port *port = port_vlan->port; 1888c2ecf20Sopenharmony_ci struct prestera_bridge_vlan *br_vlan; 1898c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port; 1908c2ecf20Sopenharmony_ci bool last_port, last_vlan; 1918c2ecf20Sopenharmony_ci u16 vid = port_vlan->vid; 1928c2ecf20Sopenharmony_ci int port_count; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci br_port = port_vlan->br_port; 1958c2ecf20Sopenharmony_ci port_count = prestera_bridge_vlan_port_count(br_port->bridge, vid); 1968c2ecf20Sopenharmony_ci br_vlan = prestera_bridge_vlan_by_vid(br_port, vid); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci last_vlan = list_is_singular(&br_port->vlan_list); 1998c2ecf20Sopenharmony_ci last_port = port_count == 1; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (last_vlan) 2028c2ecf20Sopenharmony_ci prestera_hw_fdb_flush_port(port, fdb_flush_mode); 2038c2ecf20Sopenharmony_ci else if (last_port) 2048c2ecf20Sopenharmony_ci prestera_hw_fdb_flush_vlan(port->sw, vid, fdb_flush_mode); 2058c2ecf20Sopenharmony_ci else 2068c2ecf20Sopenharmony_ci prestera_hw_fdb_flush_port_vlan(port, vid, fdb_flush_mode); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci list_del(&port_vlan->br_vlan_head); 2098c2ecf20Sopenharmony_ci prestera_bridge_vlan_put(br_vlan); 2108c2ecf20Sopenharmony_ci prestera_bridge_port_put(br_port); 2118c2ecf20Sopenharmony_ci port_vlan->br_port = NULL; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void prestera_port_vlan_destroy(struct prestera_port_vlan *port_vlan) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct prestera_port *port = port_vlan->port; 2178c2ecf20Sopenharmony_ci u16 vid = port_vlan->vid; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (port_vlan->br_port) 2208c2ecf20Sopenharmony_ci prestera_port_vlan_bridge_leave(port_vlan); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci prestera_hw_vlan_port_set(port, vid, false, false); 2238c2ecf20Sopenharmony_ci list_del(&port_vlan->port_head); 2248c2ecf20Sopenharmony_ci kfree(port_vlan); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic struct prestera_bridge * 2288c2ecf20Sopenharmony_ciprestera_bridge_create(struct prestera_switchdev *swdev, struct net_device *dev) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci bool vlan_enabled = br_vlan_enabled(dev); 2318c2ecf20Sopenharmony_ci struct prestera_bridge *bridge; 2328c2ecf20Sopenharmony_ci u16 bridge_id; 2338c2ecf20Sopenharmony_ci int err; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (vlan_enabled && swdev->bridge_8021q_exists) { 2368c2ecf20Sopenharmony_ci netdev_err(dev, "Only one VLAN-aware bridge is supported\n"); 2378c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); 2418c2ecf20Sopenharmony_ci if (!bridge) 2428c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (vlan_enabled) { 2458c2ecf20Sopenharmony_ci swdev->bridge_8021q_exists = true; 2468c2ecf20Sopenharmony_ci } else { 2478c2ecf20Sopenharmony_ci err = prestera_hw_bridge_create(swdev->sw, &bridge_id); 2488c2ecf20Sopenharmony_ci if (err) { 2498c2ecf20Sopenharmony_ci kfree(bridge); 2508c2ecf20Sopenharmony_ci return ERR_PTR(err); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci bridge->bridge_id = bridge_id; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci bridge->vlan_enabled = vlan_enabled; 2578c2ecf20Sopenharmony_ci bridge->swdev = swdev; 2588c2ecf20Sopenharmony_ci bridge->dev = dev; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bridge->port_list); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci list_add(&bridge->head, &swdev->bridge_list); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return bridge; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic void prestera_bridge_destroy(struct prestera_bridge *bridge) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct prestera_switchdev *swdev = bridge->swdev; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci list_del(&bridge->head); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (bridge->vlan_enabled) 2748c2ecf20Sopenharmony_ci swdev->bridge_8021q_exists = false; 2758c2ecf20Sopenharmony_ci else 2768c2ecf20Sopenharmony_ci prestera_hw_bridge_delete(swdev->sw, bridge->bridge_id); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&bridge->port_list)); 2798c2ecf20Sopenharmony_ci kfree(bridge); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic void prestera_bridge_put(struct prestera_bridge *bridge) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci if (list_empty(&bridge->port_list)) 2858c2ecf20Sopenharmony_ci prestera_bridge_destroy(bridge); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic 2898c2ecf20Sopenharmony_cistruct prestera_bridge *prestera_bridge_by_dev(struct prestera_switchdev *swdev, 2908c2ecf20Sopenharmony_ci const struct net_device *dev) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct prestera_bridge *bridge; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci list_for_each_entry(bridge, &swdev->bridge_list, head) 2958c2ecf20Sopenharmony_ci if (bridge->dev == dev) 2968c2ecf20Sopenharmony_ci return bridge; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return NULL; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic struct prestera_bridge_port * 3028c2ecf20Sopenharmony_ci__prestera_bridge_port_by_dev(struct prestera_bridge *bridge, 3038c2ecf20Sopenharmony_ci struct net_device *dev) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci list_for_each_entry(br_port, &bridge->port_list, head) { 3088c2ecf20Sopenharmony_ci if (br_port->dev == dev) 3098c2ecf20Sopenharmony_ci return br_port; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci return NULL; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic struct prestera_bridge_port * 3168c2ecf20Sopenharmony_ciprestera_bridge_port_by_dev(struct prestera_switchdev *swdev, 3178c2ecf20Sopenharmony_ci struct net_device *dev) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct net_device *br_dev = netdev_master_upper_dev_get(dev); 3208c2ecf20Sopenharmony_ci struct prestera_bridge *bridge; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (!br_dev) 3238c2ecf20Sopenharmony_ci return NULL; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci bridge = prestera_bridge_by_dev(swdev, br_dev); 3268c2ecf20Sopenharmony_ci if (!bridge) 3278c2ecf20Sopenharmony_ci return NULL; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return __prestera_bridge_port_by_dev(bridge, dev); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic struct prestera_bridge_port * 3338c2ecf20Sopenharmony_ciprestera_bridge_port_create(struct prestera_bridge *bridge, 3348c2ecf20Sopenharmony_ci struct net_device *dev) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci br_port = kzalloc(sizeof(*br_port), GFP_KERNEL); 3398c2ecf20Sopenharmony_ci if (!br_port) 3408c2ecf20Sopenharmony_ci return NULL; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci br_port->flags = BR_LEARNING | BR_FLOOD | BR_LEARNING_SYNC | 3438c2ecf20Sopenharmony_ci BR_MCAST_FLOOD; 3448c2ecf20Sopenharmony_ci br_port->stp_state = BR_STATE_DISABLED; 3458c2ecf20Sopenharmony_ci refcount_set(&br_port->ref_count, 1); 3468c2ecf20Sopenharmony_ci br_port->bridge = bridge; 3478c2ecf20Sopenharmony_ci br_port->dev = dev; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&br_port->vlan_list); 3508c2ecf20Sopenharmony_ci list_add(&br_port->head, &bridge->port_list); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return br_port; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic void 3568c2ecf20Sopenharmony_ciprestera_bridge_port_destroy(struct prestera_bridge_port *br_port) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci list_del(&br_port->head); 3598c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&br_port->vlan_list)); 3608c2ecf20Sopenharmony_ci kfree(br_port); 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic void prestera_bridge_port_get(struct prestera_bridge_port *br_port) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci refcount_inc(&br_port->ref_count); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic void prestera_bridge_port_put(struct prestera_bridge_port *br_port) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct prestera_bridge *bridge = br_port->bridge; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (refcount_dec_and_test(&br_port->ref_count)) { 3738c2ecf20Sopenharmony_ci prestera_bridge_port_destroy(br_port); 3748c2ecf20Sopenharmony_ci prestera_bridge_put(bridge); 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic struct prestera_bridge_port * 3798c2ecf20Sopenharmony_ciprestera_bridge_port_add(struct prestera_bridge *bridge, struct net_device *dev) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci br_port = __prestera_bridge_port_by_dev(bridge, dev); 3848c2ecf20Sopenharmony_ci if (br_port) { 3858c2ecf20Sopenharmony_ci prestera_bridge_port_get(br_port); 3868c2ecf20Sopenharmony_ci return br_port; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci br_port = prestera_bridge_port_create(bridge, dev); 3908c2ecf20Sopenharmony_ci if (!br_port) 3918c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return br_port; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic int 3978c2ecf20Sopenharmony_ciprestera_bridge_1d_port_join(struct prestera_bridge_port *br_port) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct prestera_port *port = netdev_priv(br_port->dev); 4008c2ecf20Sopenharmony_ci struct prestera_bridge *bridge = br_port->bridge; 4018c2ecf20Sopenharmony_ci int err; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci err = prestera_hw_bridge_port_add(port, bridge->bridge_id); 4048c2ecf20Sopenharmony_ci if (err) 4058c2ecf20Sopenharmony_ci return err; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci err = prestera_hw_port_flood_set(port, br_port->flags & BR_FLOOD); 4088c2ecf20Sopenharmony_ci if (err) 4098c2ecf20Sopenharmony_ci goto err_port_flood_set; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci err = prestera_hw_port_learning_set(port, br_port->flags & BR_LEARNING); 4128c2ecf20Sopenharmony_ci if (err) 4138c2ecf20Sopenharmony_ci goto err_port_learning_set; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci return 0; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cierr_port_learning_set: 4188c2ecf20Sopenharmony_ci prestera_hw_port_flood_set(port, false); 4198c2ecf20Sopenharmony_cierr_port_flood_set: 4208c2ecf20Sopenharmony_ci prestera_hw_bridge_port_delete(port, bridge->bridge_id); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return err; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic int prestera_port_bridge_join(struct prestera_port *port, 4268c2ecf20Sopenharmony_ci struct net_device *upper) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct prestera_switchdev *swdev = port->sw->swdev; 4298c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port; 4308c2ecf20Sopenharmony_ci struct prestera_bridge *bridge; 4318c2ecf20Sopenharmony_ci int err; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci bridge = prestera_bridge_by_dev(swdev, upper); 4348c2ecf20Sopenharmony_ci if (!bridge) { 4358c2ecf20Sopenharmony_ci bridge = prestera_bridge_create(swdev, upper); 4368c2ecf20Sopenharmony_ci if (IS_ERR(bridge)) 4378c2ecf20Sopenharmony_ci return PTR_ERR(bridge); 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci br_port = prestera_bridge_port_add(bridge, port->dev); 4418c2ecf20Sopenharmony_ci if (IS_ERR(br_port)) { 4428c2ecf20Sopenharmony_ci prestera_bridge_put(bridge); 4438c2ecf20Sopenharmony_ci return PTR_ERR(br_port); 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (bridge->vlan_enabled) 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci err = prestera_bridge_1d_port_join(br_port); 4508c2ecf20Sopenharmony_ci if (err) 4518c2ecf20Sopenharmony_ci goto err_port_join; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cierr_port_join: 4568c2ecf20Sopenharmony_ci prestera_bridge_port_put(br_port); 4578c2ecf20Sopenharmony_ci return err; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic void prestera_bridge_1q_port_leave(struct prestera_bridge_port *br_port) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct prestera_port *port = netdev_priv(br_port->dev); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci prestera_hw_fdb_flush_port(port, PRESTERA_FDB_FLUSH_MODE_ALL); 4658c2ecf20Sopenharmony_ci prestera_port_pvid_set(port, PRESTERA_DEFAULT_VID); 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic void prestera_bridge_1d_port_leave(struct prestera_bridge_port *br_port) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct prestera_port *port = netdev_priv(br_port->dev); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci prestera_hw_fdb_flush_port(port, PRESTERA_FDB_FLUSH_MODE_ALL); 4738c2ecf20Sopenharmony_ci prestera_hw_bridge_port_delete(port, br_port->bridge->bridge_id); 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic int prestera_port_vid_stp_set(struct prestera_port *port, u16 vid, 4778c2ecf20Sopenharmony_ci u8 state) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci u8 hw_state = state; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci switch (state) { 4828c2ecf20Sopenharmony_ci case BR_STATE_DISABLED: 4838c2ecf20Sopenharmony_ci hw_state = PRESTERA_STP_DISABLED; 4848c2ecf20Sopenharmony_ci break; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci case BR_STATE_BLOCKING: 4878c2ecf20Sopenharmony_ci case BR_STATE_LISTENING: 4888c2ecf20Sopenharmony_ci hw_state = PRESTERA_STP_BLOCK_LISTEN; 4898c2ecf20Sopenharmony_ci break; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci case BR_STATE_LEARNING: 4928c2ecf20Sopenharmony_ci hw_state = PRESTERA_STP_LEARN; 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci case BR_STATE_FORWARDING: 4968c2ecf20Sopenharmony_ci hw_state = PRESTERA_STP_FORWARD; 4978c2ecf20Sopenharmony_ci break; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci default: 5008c2ecf20Sopenharmony_ci return -EINVAL; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return prestera_hw_vlan_port_stp_set(port, vid, hw_state); 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic void prestera_port_bridge_leave(struct prestera_port *port, 5078c2ecf20Sopenharmony_ci struct net_device *upper) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct prestera_switchdev *swdev = port->sw->swdev; 5108c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port; 5118c2ecf20Sopenharmony_ci struct prestera_bridge *bridge; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci bridge = prestera_bridge_by_dev(swdev, upper); 5148c2ecf20Sopenharmony_ci if (!bridge) 5158c2ecf20Sopenharmony_ci return; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci br_port = __prestera_bridge_port_by_dev(bridge, port->dev); 5188c2ecf20Sopenharmony_ci if (!br_port) 5198c2ecf20Sopenharmony_ci return; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci bridge = br_port->bridge; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (bridge->vlan_enabled) 5248c2ecf20Sopenharmony_ci prestera_bridge_1q_port_leave(br_port); 5258c2ecf20Sopenharmony_ci else 5268c2ecf20Sopenharmony_ci prestera_bridge_1d_port_leave(br_port); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci prestera_hw_port_learning_set(port, false); 5298c2ecf20Sopenharmony_ci prestera_hw_port_flood_set(port, false); 5308c2ecf20Sopenharmony_ci prestera_port_vid_stp_set(port, PRESTERA_VID_ALL, BR_STATE_FORWARDING); 5318c2ecf20Sopenharmony_ci prestera_bridge_port_put(br_port); 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ciint prestera_bridge_port_event(struct net_device *dev, unsigned long event, 5358c2ecf20Sopenharmony_ci void *ptr) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct netdev_notifier_changeupper_info *info = ptr; 5388c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack; 5398c2ecf20Sopenharmony_ci struct prestera_port *port; 5408c2ecf20Sopenharmony_ci struct net_device *upper; 5418c2ecf20Sopenharmony_ci int err; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci extack = netdev_notifier_info_to_extack(&info->info); 5448c2ecf20Sopenharmony_ci port = netdev_priv(dev); 5458c2ecf20Sopenharmony_ci upper = info->upper_dev; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci switch (event) { 5488c2ecf20Sopenharmony_ci case NETDEV_PRECHANGEUPPER: 5498c2ecf20Sopenharmony_ci if (!netif_is_bridge_master(upper)) { 5508c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type"); 5518c2ecf20Sopenharmony_ci return -EINVAL; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (!info->linking) 5558c2ecf20Sopenharmony_ci break; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (netdev_has_any_upper_dev(upper)) { 5588c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Upper device is already enslaved"); 5598c2ecf20Sopenharmony_ci return -EINVAL; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci break; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci case NETDEV_CHANGEUPPER: 5648c2ecf20Sopenharmony_ci if (!netif_is_bridge_master(upper)) 5658c2ecf20Sopenharmony_ci break; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (info->linking) { 5688c2ecf20Sopenharmony_ci err = prestera_port_bridge_join(port, upper); 5698c2ecf20Sopenharmony_ci if (err) 5708c2ecf20Sopenharmony_ci return err; 5718c2ecf20Sopenharmony_ci } else { 5728c2ecf20Sopenharmony_ci prestera_port_bridge_leave(port, upper); 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci break; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci return 0; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic int prestera_port_attr_br_flags_set(struct prestera_port *port, 5818c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 5828c2ecf20Sopenharmony_ci struct net_device *dev, 5838c2ecf20Sopenharmony_ci unsigned long flags) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port; 5868c2ecf20Sopenharmony_ci int err; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) 5898c2ecf20Sopenharmony_ci return 0; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci br_port = prestera_bridge_port_by_dev(port->sw->swdev, dev); 5928c2ecf20Sopenharmony_ci if (!br_port) 5938c2ecf20Sopenharmony_ci return 0; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci err = prestera_hw_port_flood_set(port, flags & BR_FLOOD); 5968c2ecf20Sopenharmony_ci if (err) 5978c2ecf20Sopenharmony_ci return err; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci err = prestera_hw_port_learning_set(port, flags & BR_LEARNING); 6008c2ecf20Sopenharmony_ci if (err) 6018c2ecf20Sopenharmony_ci return err; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci memcpy(&br_port->flags, &flags, sizeof(flags)); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci return 0; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic int prestera_port_attr_br_ageing_set(struct prestera_port *port, 6098c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 6108c2ecf20Sopenharmony_ci unsigned long ageing_clock_t) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t); 6138c2ecf20Sopenharmony_ci u32 ageing_time_ms = jiffies_to_msecs(ageing_jiffies); 6148c2ecf20Sopenharmony_ci struct prestera_switch *sw = port->sw; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) { 6178c2ecf20Sopenharmony_ci if (ageing_time_ms < PRESTERA_MIN_AGEING_TIME_MS || 6188c2ecf20Sopenharmony_ci ageing_time_ms > PRESTERA_MAX_AGEING_TIME_MS) 6198c2ecf20Sopenharmony_ci return -ERANGE; 6208c2ecf20Sopenharmony_ci else 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci return prestera_hw_switch_ageing_set(sw, ageing_time_ms); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic int prestera_port_attr_br_vlan_set(struct prestera_port *port, 6288c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 6298c2ecf20Sopenharmony_ci struct net_device *dev, 6308c2ecf20Sopenharmony_ci bool vlan_enabled) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci struct prestera_switch *sw = port->sw; 6338c2ecf20Sopenharmony_ci struct prestera_bridge *bridge; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (!switchdev_trans_ph_prepare(trans)) 6368c2ecf20Sopenharmony_ci return 0; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci bridge = prestera_bridge_by_dev(sw->swdev, dev); 6398c2ecf20Sopenharmony_ci if (WARN_ON(!bridge)) 6408c2ecf20Sopenharmony_ci return -EINVAL; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (bridge->vlan_enabled == vlan_enabled) 6438c2ecf20Sopenharmony_ci return 0; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci netdev_err(bridge->dev, "VLAN filtering can't be changed for existing bridge\n"); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci return -EINVAL; 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic int prestera_port_bridge_vlan_stp_set(struct prestera_port *port, 6518c2ecf20Sopenharmony_ci struct prestera_bridge_vlan *br_vlan, 6528c2ecf20Sopenharmony_ci u8 state) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci struct prestera_port_vlan *port_vlan; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci list_for_each_entry(port_vlan, &br_vlan->port_vlan_list, br_vlan_head) { 6578c2ecf20Sopenharmony_ci if (port_vlan->port != port) 6588c2ecf20Sopenharmony_ci continue; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci return prestera_port_vid_stp_set(port, br_vlan->vid, state); 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci return 0; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic int presterar_port_attr_stp_state_set(struct prestera_port *port, 6678c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 6688c2ecf20Sopenharmony_ci struct net_device *dev, 6698c2ecf20Sopenharmony_ci u8 state) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port; 6728c2ecf20Sopenharmony_ci struct prestera_bridge_vlan *br_vlan; 6738c2ecf20Sopenharmony_ci int err; 6748c2ecf20Sopenharmony_ci u16 vid; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) 6778c2ecf20Sopenharmony_ci return 0; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci br_port = prestera_bridge_port_by_dev(port->sw->swdev, dev); 6808c2ecf20Sopenharmony_ci if (!br_port) 6818c2ecf20Sopenharmony_ci return 0; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (!br_port->bridge->vlan_enabled) { 6848c2ecf20Sopenharmony_ci vid = br_port->bridge->bridge_id; 6858c2ecf20Sopenharmony_ci err = prestera_port_vid_stp_set(port, vid, state); 6868c2ecf20Sopenharmony_ci if (err) 6878c2ecf20Sopenharmony_ci goto err_port_stp_set; 6888c2ecf20Sopenharmony_ci } else { 6898c2ecf20Sopenharmony_ci list_for_each_entry(br_vlan, &br_port->vlan_list, head) { 6908c2ecf20Sopenharmony_ci err = prestera_port_bridge_vlan_stp_set(port, br_vlan, 6918c2ecf20Sopenharmony_ci state); 6928c2ecf20Sopenharmony_ci if (err) 6938c2ecf20Sopenharmony_ci goto err_port_vlan_stp_set; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci br_port->stp_state = state; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci return 0; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cierr_port_vlan_stp_set: 7028c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(br_vlan, &br_port->vlan_list, head) 7038c2ecf20Sopenharmony_ci prestera_port_bridge_vlan_stp_set(port, br_vlan, br_port->stp_state); 7048c2ecf20Sopenharmony_ci return err; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cierr_port_stp_set: 7078c2ecf20Sopenharmony_ci prestera_port_vid_stp_set(port, vid, br_port->stp_state); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci return err; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic int prestera_port_obj_attr_set(struct net_device *dev, 7138c2ecf20Sopenharmony_ci const struct switchdev_attr *attr, 7148c2ecf20Sopenharmony_ci struct switchdev_trans *trans) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 7178c2ecf20Sopenharmony_ci int err = 0; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci switch (attr->id) { 7208c2ecf20Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_STP_STATE: 7218c2ecf20Sopenharmony_ci err = presterar_port_attr_stp_state_set(port, trans, 7228c2ecf20Sopenharmony_ci attr->orig_dev, 7238c2ecf20Sopenharmony_ci attr->u.stp_state); 7248c2ecf20Sopenharmony_ci break; 7258c2ecf20Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: 7268c2ecf20Sopenharmony_ci if (attr->u.brport_flags & 7278c2ecf20Sopenharmony_ci ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) 7288c2ecf20Sopenharmony_ci err = -EINVAL; 7298c2ecf20Sopenharmony_ci break; 7308c2ecf20Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: 7318c2ecf20Sopenharmony_ci err = prestera_port_attr_br_flags_set(port, trans, 7328c2ecf20Sopenharmony_ci attr->orig_dev, 7338c2ecf20Sopenharmony_ci attr->u.brport_flags); 7348c2ecf20Sopenharmony_ci break; 7358c2ecf20Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: 7368c2ecf20Sopenharmony_ci err = prestera_port_attr_br_ageing_set(port, trans, 7378c2ecf20Sopenharmony_ci attr->u.ageing_time); 7388c2ecf20Sopenharmony_ci break; 7398c2ecf20Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: 7408c2ecf20Sopenharmony_ci err = prestera_port_attr_br_vlan_set(port, trans, 7418c2ecf20Sopenharmony_ci attr->orig_dev, 7428c2ecf20Sopenharmony_ci attr->u.vlan_filtering); 7438c2ecf20Sopenharmony_ci break; 7448c2ecf20Sopenharmony_ci default: 7458c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci return err; 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic void 7528c2ecf20Sopenharmony_ciprestera_fdb_offload_notify(struct prestera_port *port, 7538c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info *info) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info send_info; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci send_info.addr = info->addr; 7588c2ecf20Sopenharmony_ci send_info.vid = info->vid; 7598c2ecf20Sopenharmony_ci send_info.offloaded = true; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, port->dev, 7628c2ecf20Sopenharmony_ci &send_info.info, NULL); 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic int prestera_port_fdb_set(struct prestera_port *port, 7668c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info, 7678c2ecf20Sopenharmony_ci bool adding) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci struct prestera_switch *sw = port->sw; 7708c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port; 7718c2ecf20Sopenharmony_ci struct prestera_bridge *bridge; 7728c2ecf20Sopenharmony_ci int err; 7738c2ecf20Sopenharmony_ci u16 vid; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci br_port = prestera_bridge_port_by_dev(sw->swdev, port->dev); 7768c2ecf20Sopenharmony_ci if (!br_port) 7778c2ecf20Sopenharmony_ci return -EINVAL; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci bridge = br_port->bridge; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (bridge->vlan_enabled) 7828c2ecf20Sopenharmony_ci vid = fdb_info->vid; 7838c2ecf20Sopenharmony_ci else 7848c2ecf20Sopenharmony_ci vid = bridge->bridge_id; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (adding) 7878c2ecf20Sopenharmony_ci err = prestera_hw_fdb_add(port, fdb_info->addr, vid, false); 7888c2ecf20Sopenharmony_ci else 7898c2ecf20Sopenharmony_ci err = prestera_hw_fdb_del(port, fdb_info->addr, vid); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci return err; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic void prestera_fdb_event_work(struct work_struct *work) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info; 7978c2ecf20Sopenharmony_ci struct prestera_fdb_event_work *swdev_work; 7988c2ecf20Sopenharmony_ci struct prestera_port *port; 7998c2ecf20Sopenharmony_ci struct net_device *dev; 8008c2ecf20Sopenharmony_ci int err; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci swdev_work = container_of(work, struct prestera_fdb_event_work, work); 8038c2ecf20Sopenharmony_ci dev = swdev_work->dev; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci rtnl_lock(); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci port = prestera_port_dev_lower_find(dev); 8088c2ecf20Sopenharmony_ci if (!port) 8098c2ecf20Sopenharmony_ci goto out_unlock; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci switch (swdev_work->event) { 8128c2ecf20Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 8138c2ecf20Sopenharmony_ci fdb_info = &swdev_work->fdb_info; 8148c2ecf20Sopenharmony_ci if (!fdb_info->added_by_user) 8158c2ecf20Sopenharmony_ci break; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci err = prestera_port_fdb_set(port, fdb_info, true); 8188c2ecf20Sopenharmony_ci if (err) 8198c2ecf20Sopenharmony_ci break; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci prestera_fdb_offload_notify(port, fdb_info); 8228c2ecf20Sopenharmony_ci break; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 8258c2ecf20Sopenharmony_ci fdb_info = &swdev_work->fdb_info; 8268c2ecf20Sopenharmony_ci prestera_port_fdb_set(port, fdb_info, false); 8278c2ecf20Sopenharmony_ci break; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ciout_unlock: 8318c2ecf20Sopenharmony_ci rtnl_unlock(); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci kfree(swdev_work->fdb_info.addr); 8348c2ecf20Sopenharmony_ci kfree(swdev_work); 8358c2ecf20Sopenharmony_ci dev_put(dev); 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic int prestera_switchdev_event(struct notifier_block *unused, 8398c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 8428c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info; 8438c2ecf20Sopenharmony_ci struct switchdev_notifier_info *info = ptr; 8448c2ecf20Sopenharmony_ci struct prestera_fdb_event_work *swdev_work; 8458c2ecf20Sopenharmony_ci struct net_device *upper; 8468c2ecf20Sopenharmony_ci int err; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (event == SWITCHDEV_PORT_ATTR_SET) { 8498c2ecf20Sopenharmony_ci err = switchdev_handle_port_attr_set(dev, ptr, 8508c2ecf20Sopenharmony_ci prestera_netdev_check, 8518c2ecf20Sopenharmony_ci prestera_port_obj_attr_set); 8528c2ecf20Sopenharmony_ci return notifier_from_errno(err); 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (!prestera_netdev_check(dev)) 8568c2ecf20Sopenharmony_ci return NOTIFY_DONE; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci upper = netdev_master_upper_dev_get_rcu(dev); 8598c2ecf20Sopenharmony_ci if (!upper) 8608c2ecf20Sopenharmony_ci return NOTIFY_DONE; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if (!netif_is_bridge_master(upper)) 8638c2ecf20Sopenharmony_ci return NOTIFY_DONE; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci swdev_work = kzalloc(sizeof(*swdev_work), GFP_ATOMIC); 8668c2ecf20Sopenharmony_ci if (!swdev_work) 8678c2ecf20Sopenharmony_ci return NOTIFY_BAD; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci swdev_work->event = event; 8708c2ecf20Sopenharmony_ci swdev_work->dev = dev; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci switch (event) { 8738c2ecf20Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 8748c2ecf20Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 8758c2ecf20Sopenharmony_ci fdb_info = container_of(info, 8768c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info, 8778c2ecf20Sopenharmony_ci info); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci INIT_WORK(&swdev_work->work, prestera_fdb_event_work); 8808c2ecf20Sopenharmony_ci memcpy(&swdev_work->fdb_info, ptr, 8818c2ecf20Sopenharmony_ci sizeof(swdev_work->fdb_info)); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci swdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); 8848c2ecf20Sopenharmony_ci if (!swdev_work->fdb_info.addr) 8858c2ecf20Sopenharmony_ci goto out_bad; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci ether_addr_copy((u8 *)swdev_work->fdb_info.addr, 8888c2ecf20Sopenharmony_ci fdb_info->addr); 8898c2ecf20Sopenharmony_ci dev_hold(dev); 8908c2ecf20Sopenharmony_ci break; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci default: 8938c2ecf20Sopenharmony_ci kfree(swdev_work); 8948c2ecf20Sopenharmony_ci return NOTIFY_DONE; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci queue_work(swdev_wq, &swdev_work->work); 8988c2ecf20Sopenharmony_ci return NOTIFY_DONE; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ciout_bad: 9018c2ecf20Sopenharmony_ci kfree(swdev_work); 9028c2ecf20Sopenharmony_ci return NOTIFY_BAD; 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic int 9068c2ecf20Sopenharmony_ciprestera_port_vlan_bridge_join(struct prestera_port_vlan *port_vlan, 9078c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci struct prestera_port *port = port_vlan->port; 9108c2ecf20Sopenharmony_ci struct prestera_bridge_vlan *br_vlan; 9118c2ecf20Sopenharmony_ci u16 vid = port_vlan->vid; 9128c2ecf20Sopenharmony_ci int err; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (port_vlan->br_port) 9158c2ecf20Sopenharmony_ci return 0; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci err = prestera_hw_port_flood_set(port, br_port->flags & BR_FLOOD); 9188c2ecf20Sopenharmony_ci if (err) 9198c2ecf20Sopenharmony_ci return err; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci err = prestera_hw_port_learning_set(port, br_port->flags & BR_LEARNING); 9228c2ecf20Sopenharmony_ci if (err) 9238c2ecf20Sopenharmony_ci goto err_port_learning_set; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci err = prestera_port_vid_stp_set(port, vid, br_port->stp_state); 9268c2ecf20Sopenharmony_ci if (err) 9278c2ecf20Sopenharmony_ci goto err_port_vid_stp_set; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci br_vlan = prestera_bridge_vlan_by_vid(br_port, vid); 9308c2ecf20Sopenharmony_ci if (!br_vlan) { 9318c2ecf20Sopenharmony_ci br_vlan = prestera_bridge_vlan_create(br_port, vid); 9328c2ecf20Sopenharmony_ci if (!br_vlan) { 9338c2ecf20Sopenharmony_ci err = -ENOMEM; 9348c2ecf20Sopenharmony_ci goto err_bridge_vlan_get; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci list_add(&port_vlan->br_vlan_head, &br_vlan->port_vlan_list); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci prestera_bridge_port_get(br_port); 9418c2ecf20Sopenharmony_ci port_vlan->br_port = br_port; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci return 0; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_cierr_bridge_vlan_get: 9468c2ecf20Sopenharmony_ci prestera_port_vid_stp_set(port, vid, BR_STATE_FORWARDING); 9478c2ecf20Sopenharmony_cierr_port_vid_stp_set: 9488c2ecf20Sopenharmony_ci prestera_hw_port_learning_set(port, false); 9498c2ecf20Sopenharmony_cierr_port_learning_set: 9508c2ecf20Sopenharmony_ci return err; 9518c2ecf20Sopenharmony_ci} 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_cistatic int 9548c2ecf20Sopenharmony_ciprestera_bridge_port_vlan_add(struct prestera_port *port, 9558c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port, 9568c2ecf20Sopenharmony_ci u16 vid, bool is_untagged, bool is_pvid, 9578c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci struct prestera_port_vlan *port_vlan; 9608c2ecf20Sopenharmony_ci u16 old_pvid = port->pvid; 9618c2ecf20Sopenharmony_ci u16 pvid; 9628c2ecf20Sopenharmony_ci int err; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (is_pvid) 9658c2ecf20Sopenharmony_ci pvid = vid; 9668c2ecf20Sopenharmony_ci else 9678c2ecf20Sopenharmony_ci pvid = port->pvid == vid ? 0 : port->pvid; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci port_vlan = prestera_port_vlan_by_vid(port, vid); 9708c2ecf20Sopenharmony_ci if (port_vlan && port_vlan->br_port != br_port) 9718c2ecf20Sopenharmony_ci return -EEXIST; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci if (!port_vlan) { 9748c2ecf20Sopenharmony_ci port_vlan = prestera_port_vlan_create(port, vid, is_untagged); 9758c2ecf20Sopenharmony_ci if (IS_ERR(port_vlan)) 9768c2ecf20Sopenharmony_ci return PTR_ERR(port_vlan); 9778c2ecf20Sopenharmony_ci } else { 9788c2ecf20Sopenharmony_ci err = prestera_hw_vlan_port_set(port, vid, true, is_untagged); 9798c2ecf20Sopenharmony_ci if (err) 9808c2ecf20Sopenharmony_ci goto err_port_vlan_set; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci err = prestera_port_pvid_set(port, pvid); 9848c2ecf20Sopenharmony_ci if (err) 9858c2ecf20Sopenharmony_ci goto err_port_pvid_set; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci err = prestera_port_vlan_bridge_join(port_vlan, br_port); 9888c2ecf20Sopenharmony_ci if (err) 9898c2ecf20Sopenharmony_ci goto err_port_vlan_bridge_join; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci return 0; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cierr_port_vlan_bridge_join: 9948c2ecf20Sopenharmony_ci prestera_port_pvid_set(port, old_pvid); 9958c2ecf20Sopenharmony_cierr_port_pvid_set: 9968c2ecf20Sopenharmony_ci prestera_hw_vlan_port_set(port, vid, false, false); 9978c2ecf20Sopenharmony_cierr_port_vlan_set: 9988c2ecf20Sopenharmony_ci prestera_port_vlan_destroy(port_vlan); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci return err; 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic void 10048c2ecf20Sopenharmony_ciprestera_bridge_port_vlan_del(struct prestera_port *port, 10058c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port, u16 vid) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci u16 pvid = port->pvid == vid ? 0 : port->pvid; 10088c2ecf20Sopenharmony_ci struct prestera_port_vlan *port_vlan; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci port_vlan = prestera_port_vlan_by_vid(port, vid); 10118c2ecf20Sopenharmony_ci if (WARN_ON(!port_vlan)) 10128c2ecf20Sopenharmony_ci return; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci prestera_port_vlan_bridge_leave(port_vlan); 10158c2ecf20Sopenharmony_ci prestera_port_pvid_set(port, pvid); 10168c2ecf20Sopenharmony_ci prestera_port_vlan_destroy(port_vlan); 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic int prestera_port_vlans_add(struct prestera_port *port, 10208c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan, 10218c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 10228c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 10258c2ecf20Sopenharmony_ci bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; 10268c2ecf20Sopenharmony_ci struct net_device *dev = vlan->obj.orig_dev; 10278c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port; 10288c2ecf20Sopenharmony_ci struct prestera_switch *sw = port->sw; 10298c2ecf20Sopenharmony_ci struct prestera_bridge *bridge; 10308c2ecf20Sopenharmony_ci u16 vid; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci if (netif_is_bridge_master(dev)) 10338c2ecf20Sopenharmony_ci return 0; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci if (switchdev_trans_ph_commit(trans)) 10368c2ecf20Sopenharmony_ci return 0; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci br_port = prestera_bridge_port_by_dev(sw->swdev, dev); 10398c2ecf20Sopenharmony_ci if (WARN_ON(!br_port)) 10408c2ecf20Sopenharmony_ci return -EINVAL; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci bridge = br_port->bridge; 10438c2ecf20Sopenharmony_ci if (!bridge->vlan_enabled) 10448c2ecf20Sopenharmony_ci return 0; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 10478c2ecf20Sopenharmony_ci int err; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci err = prestera_bridge_port_vlan_add(port, br_port, 10508c2ecf20Sopenharmony_ci vid, flag_untagged, 10518c2ecf20Sopenharmony_ci flag_pvid, extack); 10528c2ecf20Sopenharmony_ci if (err) 10538c2ecf20Sopenharmony_ci return err; 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci return 0; 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic int prestera_port_obj_add(struct net_device *dev, 10608c2ecf20Sopenharmony_ci const struct switchdev_obj *obj, 10618c2ecf20Sopenharmony_ci struct switchdev_trans *trans, 10628c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 10638c2ecf20Sopenharmony_ci{ 10648c2ecf20Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 10658c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci switch (obj->id) { 10688c2ecf20Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 10698c2ecf20Sopenharmony_ci vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); 10708c2ecf20Sopenharmony_ci return prestera_port_vlans_add(port, vlan, trans, extack); 10718c2ecf20Sopenharmony_ci default: 10728c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_cistatic int prestera_port_vlans_del(struct prestera_port *port, 10778c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 10788c2ecf20Sopenharmony_ci{ 10798c2ecf20Sopenharmony_ci struct net_device *dev = vlan->obj.orig_dev; 10808c2ecf20Sopenharmony_ci struct prestera_bridge_port *br_port; 10818c2ecf20Sopenharmony_ci struct prestera_switch *sw = port->sw; 10828c2ecf20Sopenharmony_ci u16 vid; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci if (netif_is_bridge_master(dev)) 10858c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci br_port = prestera_bridge_port_by_dev(sw->swdev, dev); 10888c2ecf20Sopenharmony_ci if (WARN_ON(!br_port)) 10898c2ecf20Sopenharmony_ci return -EINVAL; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (!br_port->bridge->vlan_enabled) 10928c2ecf20Sopenharmony_ci return 0; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) 10958c2ecf20Sopenharmony_ci prestera_bridge_port_vlan_del(port, br_port, vid); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci return 0; 10988c2ecf20Sopenharmony_ci} 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_cistatic int prestera_port_obj_del(struct net_device *dev, 11018c2ecf20Sopenharmony_ci const struct switchdev_obj *obj) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci switch (obj->id) { 11068c2ecf20Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 11078c2ecf20Sopenharmony_ci return prestera_port_vlans_del(port, SWITCHDEV_OBJ_PORT_VLAN(obj)); 11088c2ecf20Sopenharmony_ci default: 11098c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistatic int prestera_switchdev_blk_event(struct notifier_block *unused, 11148c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 11178c2ecf20Sopenharmony_ci int err; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci switch (event) { 11208c2ecf20Sopenharmony_ci case SWITCHDEV_PORT_OBJ_ADD: 11218c2ecf20Sopenharmony_ci err = switchdev_handle_port_obj_add(dev, ptr, 11228c2ecf20Sopenharmony_ci prestera_netdev_check, 11238c2ecf20Sopenharmony_ci prestera_port_obj_add); 11248c2ecf20Sopenharmony_ci break; 11258c2ecf20Sopenharmony_ci case SWITCHDEV_PORT_OBJ_DEL: 11268c2ecf20Sopenharmony_ci err = switchdev_handle_port_obj_del(dev, ptr, 11278c2ecf20Sopenharmony_ci prestera_netdev_check, 11288c2ecf20Sopenharmony_ci prestera_port_obj_del); 11298c2ecf20Sopenharmony_ci break; 11308c2ecf20Sopenharmony_ci case SWITCHDEV_PORT_ATTR_SET: 11318c2ecf20Sopenharmony_ci err = switchdev_handle_port_attr_set(dev, ptr, 11328c2ecf20Sopenharmony_ci prestera_netdev_check, 11338c2ecf20Sopenharmony_ci prestera_port_obj_attr_set); 11348c2ecf20Sopenharmony_ci break; 11358c2ecf20Sopenharmony_ci default: 11368c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci return notifier_from_errno(err); 11408c2ecf20Sopenharmony_ci} 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_cistatic void prestera_fdb_event(struct prestera_switch *sw, 11438c2ecf20Sopenharmony_ci struct prestera_event *evt, void *arg) 11448c2ecf20Sopenharmony_ci{ 11458c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info info; 11468c2ecf20Sopenharmony_ci struct prestera_port *port; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci port = prestera_find_port(sw, evt->fdb_evt.port_id); 11498c2ecf20Sopenharmony_ci if (!port) 11508c2ecf20Sopenharmony_ci return; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci info.addr = evt->fdb_evt.data.mac; 11538c2ecf20Sopenharmony_ci info.vid = evt->fdb_evt.vid; 11548c2ecf20Sopenharmony_ci info.offloaded = true; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci rtnl_lock(); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci switch (evt->id) { 11598c2ecf20Sopenharmony_ci case PRESTERA_FDB_EVENT_LEARNED: 11608c2ecf20Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, 11618c2ecf20Sopenharmony_ci port->dev, &info.info, NULL); 11628c2ecf20Sopenharmony_ci break; 11638c2ecf20Sopenharmony_ci case PRESTERA_FDB_EVENT_AGED: 11648c2ecf20Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, 11658c2ecf20Sopenharmony_ci port->dev, &info.info, NULL); 11668c2ecf20Sopenharmony_ci break; 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci rtnl_unlock(); 11708c2ecf20Sopenharmony_ci} 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_cistatic int prestera_fdb_init(struct prestera_switch *sw) 11738c2ecf20Sopenharmony_ci{ 11748c2ecf20Sopenharmony_ci int err; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci err = prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_FDB, 11778c2ecf20Sopenharmony_ci prestera_fdb_event, NULL); 11788c2ecf20Sopenharmony_ci if (err) 11798c2ecf20Sopenharmony_ci return err; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci err = prestera_hw_switch_ageing_set(sw, PRESTERA_DEFAULT_AGEING_TIME_MS); 11828c2ecf20Sopenharmony_ci if (err) 11838c2ecf20Sopenharmony_ci goto err_ageing_set; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci return 0; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_cierr_ageing_set: 11888c2ecf20Sopenharmony_ci prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_FDB, 11898c2ecf20Sopenharmony_ci prestera_fdb_event); 11908c2ecf20Sopenharmony_ci return err; 11918c2ecf20Sopenharmony_ci} 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic void prestera_fdb_fini(struct prestera_switch *sw) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_FDB, 11968c2ecf20Sopenharmony_ci prestera_fdb_event); 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic int prestera_switchdev_handler_init(struct prestera_switchdev *swdev) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci int err; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci swdev->swdev_nb.notifier_call = prestera_switchdev_event; 12048c2ecf20Sopenharmony_ci err = register_switchdev_notifier(&swdev->swdev_nb); 12058c2ecf20Sopenharmony_ci if (err) 12068c2ecf20Sopenharmony_ci goto err_register_swdev_notifier; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci swdev->swdev_nb_blk.notifier_call = prestera_switchdev_blk_event; 12098c2ecf20Sopenharmony_ci err = register_switchdev_blocking_notifier(&swdev->swdev_nb_blk); 12108c2ecf20Sopenharmony_ci if (err) 12118c2ecf20Sopenharmony_ci goto err_register_blk_swdev_notifier; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci return 0; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_cierr_register_blk_swdev_notifier: 12168c2ecf20Sopenharmony_ci unregister_switchdev_notifier(&swdev->swdev_nb); 12178c2ecf20Sopenharmony_cierr_register_swdev_notifier: 12188c2ecf20Sopenharmony_ci destroy_workqueue(swdev_wq); 12198c2ecf20Sopenharmony_ci return err; 12208c2ecf20Sopenharmony_ci} 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_cistatic void prestera_switchdev_handler_fini(struct prestera_switchdev *swdev) 12238c2ecf20Sopenharmony_ci{ 12248c2ecf20Sopenharmony_ci unregister_switchdev_blocking_notifier(&swdev->swdev_nb_blk); 12258c2ecf20Sopenharmony_ci unregister_switchdev_notifier(&swdev->swdev_nb); 12268c2ecf20Sopenharmony_ci} 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ciint prestera_switchdev_init(struct prestera_switch *sw) 12298c2ecf20Sopenharmony_ci{ 12308c2ecf20Sopenharmony_ci struct prestera_switchdev *swdev; 12318c2ecf20Sopenharmony_ci int err; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci swdev = kzalloc(sizeof(*swdev), GFP_KERNEL); 12348c2ecf20Sopenharmony_ci if (!swdev) 12358c2ecf20Sopenharmony_ci return -ENOMEM; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci sw->swdev = swdev; 12388c2ecf20Sopenharmony_ci swdev->sw = sw; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&swdev->bridge_list); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci swdev_wq = alloc_ordered_workqueue("%s_ordered", 0, "prestera_br"); 12438c2ecf20Sopenharmony_ci if (!swdev_wq) { 12448c2ecf20Sopenharmony_ci err = -ENOMEM; 12458c2ecf20Sopenharmony_ci goto err_alloc_wq; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci err = prestera_switchdev_handler_init(swdev); 12498c2ecf20Sopenharmony_ci if (err) 12508c2ecf20Sopenharmony_ci goto err_swdev_init; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci err = prestera_fdb_init(sw); 12538c2ecf20Sopenharmony_ci if (err) 12548c2ecf20Sopenharmony_ci goto err_fdb_init; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci return 0; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_cierr_fdb_init: 12598c2ecf20Sopenharmony_cierr_swdev_init: 12608c2ecf20Sopenharmony_ci destroy_workqueue(swdev_wq); 12618c2ecf20Sopenharmony_cierr_alloc_wq: 12628c2ecf20Sopenharmony_ci kfree(swdev); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci return err; 12658c2ecf20Sopenharmony_ci} 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_civoid prestera_switchdev_fini(struct prestera_switch *sw) 12688c2ecf20Sopenharmony_ci{ 12698c2ecf20Sopenharmony_ci struct prestera_switchdev *swdev = sw->swdev; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci prestera_fdb_fini(sw); 12728c2ecf20Sopenharmony_ci prestera_switchdev_handler_fini(swdev); 12738c2ecf20Sopenharmony_ci destroy_workqueue(swdev_wq); 12748c2ecf20Sopenharmony_ci kfree(swdev); 12758c2ecf20Sopenharmony_ci} 1276