--- a/vlan/nss_vlan_mgr.c +++ b/vlan/nss_vlan_mgr.c @@ -1,9 +1,12 @@ /* ************************************************************************** * Copyright (c) 2017-2018, 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all copies. + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -154,15 +157,6 @@ static struct nss_vlan_pvt *nss_vlan_mgr return NULL; } -/* - * nss_vlan_mgr_instance_deref() - */ -static void nss_vlan_mgr_instance_deref(struct nss_vlan_pvt *v) -{ - spin_lock(&vlan_mgr_ctx.lock); - BUG_ON(!(--v->refs)); - spin_unlock(&vlan_mgr_ctx.lock); -} #ifdef NSS_VLAN_MGR_PPE_SUPPORT /* @@ -365,22 +359,16 @@ static void nss_vlan_mgr_port_role_event * nss_vlan_mgr_bond_configure_ppe() * Configure PPE for bond device */ -static int nss_vlan_mgr_bond_configure_ppe(struct nss_vlan_pvt *v, struct net_device *bond_dev) +static int nss_vlan_mgr_bond_configure_ppe(struct nss_vlan_pvt *v, struct net_device *bond_dev, uint32_t vsi) { - uint32_t vsi; int ret = 0; struct net_device *slave; int32_t port; int vlan_mgr_bond_port_role = -1; - if (ppe_vsi_alloc(NSS_VLAN_MGR_SWITCH_ID, &vsi)) { - nss_vlan_mgr_warn("%s: failed to allocate VSI for bond vlan device", bond_dev->name); - return -1; - } - if (nss_vlan_tx_vsi_attach_msg(v->nss_if, vsi) != NSS_TX_SUCCESS) { nss_vlan_mgr_warn("%s: failed to attach VSI to bond vlan interface\n", bond_dev->name); - goto free_vsi; + return -1; } /* @@ -393,7 +381,7 @@ static int nss_vlan_mgr_bond_configure_p if (!NSS_VLAN_PHY_PORT_CHK(port)) { rcu_read_unlock(); nss_vlan_mgr_warn("%s: %d is not valid physical port\n", slave->name, port); - goto free_vsi; + return -1; } /* @@ -409,7 +397,7 @@ static int nss_vlan_mgr_bond_configure_p * In case the bond interface has no slaves, we do not want to proceed further */ if (vlan_mgr_bond_port_role == -1) { - goto free_vsi; + return -1; } /* @@ -436,6 +424,12 @@ static int nss_vlan_mgr_bond_configure_p rcu_read_lock(); for_each_netdev_in_bond_rcu(bond_dev, slave) { port = nss_cmn_get_interface_number_by_dev(slave); + if (!NSS_VLAN_PHY_PORT_CHK(port)) { + rcu_read_unlock(); + nss_vlan_mgr_warn("%s: %d is not valid physical port\n", slave->name, port); + return -1; + } + ret = ppe_port_vlan_vsi_set(NSS_VLAN_MGR_SWITCH_ID, v->port[port - 1], v->ppe_svid, v->ppe_cvid, vsi); if (ret != SW_OK) { rcu_read_unlock(); @@ -471,6 +465,12 @@ static int nss_vlan_mgr_bond_configure_p rcu_read_lock(); for_each_netdev_in_bond_rcu(bond_dev, slave) { port = nss_cmn_get_interface_number_by_dev(slave); + if (!NSS_VLAN_PHY_PORT_CHK(port)) { + rcu_read_unlock(); + nss_vlan_mgr_warn("%s: %d is not valid physical port\n", slave->name, port); + return -1; + } + v->eg_xlt_rule.port_bitmap |= (1 << v->port[port - 1]); ret = fal_port_vlan_trans_adv_add(NSS_VLAN_MGR_SWITCH_ID, v->port[port - 1], FAL_PORT_VLAN_EGRESS, &v->eg_xlt_rule, &v->eg_xlt_action); @@ -490,6 +490,11 @@ static int nss_vlan_mgr_bond_configure_p for_each_netdev_in_bond_rcu(bond_dev, slave) { fal_port_qinq_role_t mode; port = nss_cmn_get_interface_number_by_dev(slave); + if (!NSS_VLAN_PHY_PORT_CHK(port)) { + rcu_read_unlock(); + nss_vlan_mgr_warn("%s: %d is not valid physical port\n", slave->name, port); + return -1; + } /* * If double tag, we should set physical port as core port @@ -513,13 +518,18 @@ static int nss_vlan_mgr_bond_configure_p ret = NSS_VLAN_PORT_ROLE_CHANGED; } - v->ppe_vsi = vsi; return ret; delete_egress_rule: rcu_read_lock(); for_each_netdev_in_bond_rcu(bond_dev, slave) { port = nss_cmn_get_interface_number_by_dev(slave); + if (!NSS_VLAN_PHY_PORT_CHK(port)) { + rcu_read_unlock(); + nss_vlan_mgr_warn("%s: %d is not valid physical port\n", slave->name, port); + return -1; + } + ret = fal_port_vlan_trans_adv_del(NSS_VLAN_MGR_SWITCH_ID, v->port[port - 1], FAL_PORT_VLAN_EGRESS, &v->eg_xlt_rule, &v->eg_xlt_action); @@ -533,6 +543,12 @@ delete_ingress_rule: rcu_read_lock(); for_each_netdev_in_bond_rcu(bond_dev, slave) { port = nss_cmn_get_interface_number_by_dev(slave); + if (!NSS_VLAN_PHY_PORT_CHK(port)) { + rcu_read_unlock(); + nss_vlan_mgr_warn("%s: %d is not valid physical port\n", slave->name, port); + return -1; + } + ret = ppe_port_vlan_vsi_set(NSS_VLAN_MGR_SWITCH_ID, v->port[port - 1], v->ppe_svid, v->ppe_cvid, PPE_VSI_INVALID); if (ret != SW_OK) { nss_vlan_mgr_warn("%px: Failed to delete ingress translation rule for port:%d, error: %d\n", v, v->port[port - 1], ret); @@ -545,30 +561,19 @@ detach_vsi: nss_vlan_mgr_warn("%px: Failed to detach vsi %d\n", v, vsi); } -free_vsi: - if (ppe_vsi_free(NSS_VLAN_MGR_SWITCH_ID, vsi)) { - nss_vlan_mgr_warn("%px: Failed to free VLAN VSI\n", v); - } - return -1; } /* * nss_vlan_mgr_configure_ppe() * Configure PPE for physical devices */ -static int nss_vlan_mgr_configure_ppe(struct nss_vlan_pvt *v, struct net_device *dev) +static int nss_vlan_mgr_configure_ppe(struct nss_vlan_pvt *v, struct net_device *dev, uint32_t vsi) { - uint32_t vsi; int ret = 0; - if (ppe_vsi_alloc(NSS_VLAN_MGR_SWITCH_ID, &vsi)) { - nss_vlan_mgr_warn("%s: failed to allocate VSI for vlan device", dev->name); - return -1; - } - if (nss_vlan_tx_vsi_attach_msg(v->nss_if, vsi) != NSS_TX_SUCCESS) { nss_vlan_mgr_warn("%s: failed to attach VSI to vlan interface\n", dev->name); - goto free_vsi; + return -1; } /* @@ -652,7 +657,6 @@ static int nss_vlan_mgr_configure_ppe(st ret = NSS_VLAN_PORT_ROLE_CHANGED; } - v->ppe_vsi = vsi; return ret; delete_egress_rule: @@ -674,16 +678,119 @@ detach_vsi: nss_vlan_mgr_warn("%px: Failed to detach vsi %d\n", v, vsi); } -free_vsi: - if (ppe_vsi_free(NSS_VLAN_MGR_SWITCH_ID, vsi)) { - nss_vlan_mgr_warn("%px: Failed to free VLAN VSI\n", v); - } - return -1; } #endif /* + * nss_vlan_mgr_instance_free() + * Destroy vlan instance + */ +static void nss_vlan_mgr_instance_free(struct nss_vlan_pvt *v) +{ +#ifdef NSS_VLAN_MGR_PPE_SUPPORT + int32_t i; + int ret = 0; +#endif + +#ifdef NSS_VLAN_MGR_PPE_SUPPORT + if (v->ppe_vsi) { + /* + * Detach VSI + */ + if (nss_vlan_tx_vsi_detach_msg(v->nss_if, v->ppe_vsi)) { + nss_vlan_mgr_warn("%px: Failed to detach vsi %d\n", v, v->ppe_vsi); + } + + /* + * Delete ingress vlan translation rule + */ + for (i = 0; i < NSS_VLAN_PHY_PORT_MAX; i++) { + if (!v->port[i]) + continue; + ret = ppe_port_vlan_vsi_set(NSS_VLAN_MGR_SWITCH_ID, v->port[i], v->ppe_svid, v->ppe_cvid, PPE_VSI_INVALID); + if (ret != SW_OK) + nss_vlan_mgr_warn("%px: Failed to delete old ingress translation rule, error: %d\n", v, ret); + } + + /* + * Delete egress vlan translation rule + */ + v->eg_xlt_rule.port_bitmap = 0; + for (i = 0; i < NSS_VLAN_PHY_PORT_MAX; i++) { + if (!v->port[i]) + continue; + v->eg_xlt_rule.port_bitmap |= (1 << v->port[i]); + ret = fal_port_vlan_trans_adv_del(NSS_VLAN_MGR_SWITCH_ID, v->port[i], + FAL_PORT_VLAN_EGRESS, + &v->eg_xlt_rule, &v->eg_xlt_action); + if (ret != SW_OK) { + nss_vlan_mgr_warn("%px: Failed to delete vlan translation rule, error:%d\n", v, ret); + } + } + + /* + * We will always have a VSI since this is allocated in beginning + * of the code. + */ + if (ppe_vsi_free(NSS_VLAN_MGR_SWITCH_ID, v->ppe_vsi)) { + nss_vlan_mgr_warn("%px: Failed to free VLAN VSI\n", v); + } + } + + /* + * Need to change the physical port role. While adding + * eth0.10.20/bond0.10.20, the role of the physical port(s) changed + * from EDGE to CORE. So, while removing eth0.10.20/bond0.10.20, the + * role of the physical port(s) should be changed from CORE to EDGE. + */ + for (i = 0; i < NSS_VLAN_PHY_PORT_MAX; i++) { + if (v->port[i]) { + if (nss_vlan_mgr_calculate_new_port_role(v->port[i], i)) { + nss_vlan_mgr_port_role_event(v->port[i], i); + } + } + } +#endif + + if (v->nss_if) { + nss_unregister_vlan_if(v->nss_if); + if (nss_dynamic_interface_dealloc_node(v->nss_if, NSS_DYNAMIC_INTERFACE_TYPE_VLAN) != NSS_TX_SUCCESS) + nss_vlan_mgr_warn("%px: Failed to dealloc vlan dynamic interface\n", v); + } + + kfree(v); +} + +/* + * nss_vlan_mgr_instance_deref() + */ +static void nss_vlan_mgr_instance_deref(struct nss_vlan_pvt *v) +{ + struct nss_vlan_pvt *parent = NULL; + spin_lock(&vlan_mgr_ctx.lock); + BUG_ON(v->refs == 0); + v->refs--; + + if (v->refs) { + spin_unlock(&vlan_mgr_ctx.lock); + return; + } + + if (!list_empty(&v->list)) { + list_del(&v->list); + } + + spin_unlock(&vlan_mgr_ctx.lock); + + parent = v->parent; + nss_vlan_mgr_instance_free(v); + + if (parent) + nss_vlan_mgr_instance_deref(parent); +} + +/* * nss_vlan_mgr_create_instance() * Create vlan instance */ @@ -816,95 +923,6 @@ static struct nss_vlan_pvt *nss_vlan_mgr } /* - * nss_vlan_mgr_instance_free() - * Destroy vlan instance - */ -static void nss_vlan_mgr_instance_free(struct nss_vlan_pvt *v) -{ -#ifdef NSS_VLAN_MGR_PPE_SUPPORT - int32_t i; - int ret = 0; -#endif - - spin_lock(&vlan_mgr_ctx.lock); - BUG_ON(--v->refs); - if (!list_empty(&v->list)) { - list_del(&v->list); - } - spin_unlock(&vlan_mgr_ctx.lock); - -#ifdef NSS_VLAN_MGR_PPE_SUPPORT - if (v->ppe_vsi) { - /* - * Detach VSI - */ - if (nss_vlan_tx_vsi_detach_msg(v->nss_if, v->ppe_vsi)) { - nss_vlan_mgr_warn("%px: Failed to detach vsi %d\n", v, v->ppe_vsi); - } - - /* - * Delete ingress vlan translation rule - */ - for (i = 0; i < NSS_VLAN_PHY_PORT_MAX; i++) { - if (!v->port[i]) - continue; - ret = ppe_port_vlan_vsi_set(NSS_VLAN_MGR_SWITCH_ID, v->port[i], v->ppe_svid, v->ppe_cvid, PPE_VSI_INVALID); - if (ret != SW_OK) - nss_vlan_mgr_warn("%px: Failed to delete old ingress translation rule, error: %d\n", v, ret); - } - - /* - * Delete egress vlan translation rule - */ - v->eg_xlt_rule.port_bitmap = 0; - for (i = 0; i < NSS_VLAN_PHY_PORT_MAX; i++) { - if (!v->port[i]) - continue; - v->eg_xlt_rule.port_bitmap |= (1 << v->port[i]); - ret = fal_port_vlan_trans_adv_del(NSS_VLAN_MGR_SWITCH_ID, v->port[i], - FAL_PORT_VLAN_EGRESS, - &v->eg_xlt_rule, &v->eg_xlt_action); - if (ret != SW_OK) { - nss_vlan_mgr_warn("%px: Failed to delete vlan translation rule, error:%d\n", v, ret); - } - } - - /* - * Free PPE VSI - */ - if (ppe_vsi_free(NSS_VLAN_MGR_SWITCH_ID, v->ppe_vsi)) { - nss_vlan_mgr_warn("%px: Failed to free VLAN VSI\n", v); - } - } - - /* - * Need to change the physical port role. While adding - * eth0.10.20/bond0.10.20, the role of the physical port(s) changed - * from EDGE to CORE. So, while removing eth0.10.20/bond0.10.20, the - * role of the physical port(s) should be changed from CORE to EDGE. - */ - for (i = 0; i < NSS_VLAN_PHY_PORT_MAX; i++) { - if (v->port[i]) { - if (nss_vlan_mgr_calculate_new_port_role(v->port[i], i)) { - nss_vlan_mgr_port_role_event(v->port[i], i); - } - } - } -#endif - - if (v->nss_if) { - nss_unregister_vlan_if(v->nss_if); - if (nss_dynamic_interface_dealloc_node(v->nss_if, NSS_DYNAMIC_INTERFACE_TYPE_VLAN) != NSS_TX_SUCCESS) - nss_vlan_mgr_warn("%px: Failed to dealloc vlan dynamic interface\n", v); - } - - if (v->parent) - nss_vlan_mgr_instance_deref(v->parent); - - kfree(v); -} - -/* * nss_vlan_mgr_changemtu_event() */ static int nss_vlan_mgr_changemtu_event(struct netdev_notifier_info *info) @@ -979,6 +997,7 @@ static int nss_vlan_mgr_register_event(s struct nss_vlan_pvt *v; int if_num; #ifdef NSS_VLAN_MGR_PPE_SUPPORT + uint32_t vsi; int ret; #endif uint32_t vlan_tag; @@ -995,19 +1014,25 @@ static int nss_vlan_mgr_register_event(s if (!v) return NOTIFY_DONE; + /* + * Allocate the VSI here. + */ +#ifdef NSS_VLAN_MGR_PPE_SUPPORT + if (ppe_vsi_alloc(NSS_VLAN_MGR_SWITCH_ID, &vsi)) { + nss_vlan_mgr_warn("%s: failed to allocate VSI for vlan device", dev->name); + return NOTIFY_DONE; + } +#endif + if_num = nss_dynamic_interface_alloc_node(NSS_DYNAMIC_INTERFACE_TYPE_VLAN); if (if_num < 0) { nss_vlan_mgr_warn("%s: failed to alloc NSS dynamic interface\n", dev->name); - nss_vlan_mgr_instance_free(v); - return NOTIFY_DONE; + goto vsi_alloc_free; } if (!nss_register_vlan_if(if_num, NULL, dev, 0, v)) { nss_vlan_mgr_warn("%s: failed to register NSS dynamic interface", dev->name); - if (nss_dynamic_interface_dealloc_node(if_num, NSS_DYNAMIC_INTERFACE_TYPE_VLAN) != NSS_TX_SUCCESS) - nss_vlan_mgr_warn("%px: Failed to dealloc vlan dynamic interface\n", v); - nss_vlan_mgr_instance_free(v); - return NOTIFY_DONE; + goto free_dynamic_interface; } v->nss_if = if_num; @@ -1021,26 +1046,25 @@ static int nss_vlan_mgr_register_event(s #ifdef NSS_VLAN_MGR_PPE_SUPPORT if (!is_bond_master) - ret = nss_vlan_mgr_configure_ppe(v, dev); + ret = nss_vlan_mgr_configure_ppe(v, dev, vsi); else - ret = nss_vlan_mgr_bond_configure_ppe(v, real_dev); + ret = nss_vlan_mgr_bond_configure_ppe(v, real_dev, vsi); if (ret < 0) { - nss_vlan_mgr_instance_free(v); - return NOTIFY_DONE; + goto vlan_instance_free; } + + v->ppe_vsi = vsi; #endif if (nss_vlan_tx_set_mac_addr_msg(v->nss_if, v->dev_addr) != NSS_TX_SUCCESS) { nss_vlan_mgr_warn("%s: failed to set mac_addr msg\n", dev->name); - nss_vlan_mgr_instance_free(v); - return NOTIFY_DONE; + goto vlan_instance_free; } if (nss_vlan_tx_set_mtu_msg(v->nss_if, v->mtu) != NSS_TX_SUCCESS) { nss_vlan_mgr_warn("%s: failed to set mtu msg\n", dev->name); - nss_vlan_mgr_instance_free(v); - return NOTIFY_DONE; + goto vlan_instance_free; } vlan_tag = (v->tpid << NSS_VLAN_TPID_SHIFT | v->vid); @@ -1049,8 +1073,7 @@ static int nss_vlan_mgr_register_event(s (v->parent ? v->parent->nss_if : port_if), port_if) != NSS_TX_SUCCESS) { nss_vlan_mgr_warn("%s: failed to add vlan in nss\n", dev->name); - nss_vlan_mgr_instance_free(v); - return NOTIFY_DONE; + goto vlan_instance_free; } spin_lock(&vlan_mgr_ctx.lock); @@ -1078,6 +1101,21 @@ static int nss_vlan_mgr_register_event(s } #endif return NOTIFY_DONE; + +free_dynamic_interface: + if (nss_dynamic_interface_dealloc_node(if_num, NSS_DYNAMIC_INTERFACE_TYPE_VLAN) != NSS_TX_SUCCESS) + nss_vlan_mgr_warn("%px: Failed to dealloc vlan dynamic interface\n", v); + +vsi_alloc_free: +#ifdef NSS_VLAN_MGR_PPE_SUPPORT + if (ppe_vsi_free(NSS_VLAN_MGR_SWITCH_ID, v->ppe_vsi)) { + nss_vlan_mgr_warn("%px: Failed to free VLAN VSI\n", v); + } +#endif + +vlan_instance_free: + nss_vlan_mgr_instance_free(v); + return NOTIFY_DONE; } /* @@ -1102,9 +1140,9 @@ static int nss_vlan_mgr_unregister_event nss_vlan_mgr_instance_deref(v); /* - * Free instance + * Release reference take during register_event */ - nss_vlan_mgr_instance_free(v); + nss_vlan_mgr_instance_deref(v); return NOTIFY_DONE; }