| /****************************************************************************** |
| * |
| * <:copyright-BRCM:2016:DUAL/GPL:standard |
| * |
| * Copyright (c) 2016 Broadcom |
| * All Rights Reserved |
| * |
| * Unless you and Broadcom execute a separate written software license |
| * agreement governing use of this software, this software is licensed |
| * to you under the terms of the GNU General Public License version 2 |
| * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, |
| * with the following added to such license: |
| * |
| * As a special exception, the copyright holders of this software give |
| * you permission to link this software with independent modules, and |
| * to copy and distribute the resulting executable under terms of your |
| * choice, provided that you also meet, for each linked independent |
| * module, the terms and conditions of the license of that module. |
| * An independent module is a module which is not derived from this |
| * software. The special exception does not apply to any modifications |
| * of the software. |
| * |
| * Not withstanding the above, under no circumstances may you combine |
| * this software in any way with any other Broadcom software provided |
| * under a license other than the GPL, without Broadcom's express prior |
| * written consent. |
| * |
| * :> |
| * |
| *****************************************************************************/ |
| |
| /** |
| * @file bal_dpp_vswitch.c |
| * @brief BAL Switch util helper functions that handle vswitch service requests |
| * @addtogroup sw_util |
| */ |
| |
| /*@{*/ |
| #include <bal_common.h> |
| #include <bcm_dev_log.h> |
| #include <bal_msg.h> |
| #include <bal_utils_msg.h> |
| #include "bcmos_errno.h" |
| |
| #ifndef TEST_SW_UTIL_LOOPBACK |
| #include <bcm/types.h> |
| #include <sal/core/libc.h> |
| #ifndef sal_memset |
| #define sal_memset memset |
| #endif |
| #include <bcm/port.h> |
| #include <bcm/vlan.h> |
| #include <bcm/error.h> |
| #include <bcm/vswitch.h> |
| |
| #include "bal_switch_flow.h" |
| #include "bal_dpp_vswitch.h" |
| #include "bal_switch_util.h" |
| |
| /* A local link list to keep trak of virtual switch service */ |
| TAILQ_HEAD(bal_sw_vsi_list_head, bal_sw_vsi_service) g_swutil_vsi_list; |
| |
| /** |
| * @brief The vsi list init function prepare a link list to keep track of |
| * vsi service in the switch util |
| * |
| * @return error code |
| */ |
| bcmos_errno bal_sw_util_vsi_list_init(void) |
| { |
| static uint32_t g_vsi_inited = 0; |
| if(g_vsi_inited == 0) |
| { |
| TAILQ_INIT(&g_swutil_vsi_list); |
| g_vsi_inited = 1; |
| } |
| return BCM_ERR_OK; |
| } |
| |
| /** |
| * @brief The vsi list search function by LIF tag |
| * |
| * @param p_lif_tag a pointer to the packet tag that need to match the entry in the list |
| * @param p_svc_indx a pointer to store the created service tag index within the vsi |
| |
| * @return pointer to an element in the list, NULL if failed |
| */ |
| static bal_sw_vsi_service *bal_sw_util_dpp_vsi_service_get_by_tag(bal_sw_lif_pkt_tag *p_lif_tag, uint32_t *p_svc_indx) |
| { |
| bal_sw_vsi_service *p_entry, *p_temp; |
| int lif_match = 0, svc_idx; |
| |
| if(p_svc_indx == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "call VSI service get tag with invalid parameter\n"); |
| return NULL; |
| } |
| |
| /* clear the storage area */ |
| *p_svc_indx = 0; |
| |
| TAILQ_FOREACH_SAFE(p_entry, &g_swutil_vsi_list, next_service, p_temp) |
| { |
| for(svc_idx = 0; svc_idx < p_entry->num_tag; svc_idx++) |
| { |
| if( p_entry->pkt_tag[svc_idx].type == p_lif_tag->type) |
| { |
| switch (p_lif_tag->type) |
| { |
| case BCMBAL_PKT_TAG_TYPE_SINGLE_TAG: |
| if( p_lif_tag->o_vid == p_entry->pkt_tag[svc_idx].o_vid) |
| { |
| lif_match = 1; |
| } |
| break; |
| case BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG: |
| if( p_lif_tag->o_vid == p_entry->pkt_tag[svc_idx].o_vid && p_lif_tag->i_vid == p_entry->pkt_tag[svc_idx].i_vid) |
| { |
| lif_match = 1; |
| } |
| break; |
| case BCMBAL_PKT_TAG_TYPE_UNTAGGED: |
| lif_match = 1; |
| break; |
| default: |
| BCM_LOG(ERROR, log_id_sw_util, "Unsupported packet type %d in LIF info\n", p_lif_tag->type); |
| return NULL; |
| } |
| if(lif_match) |
| { |
| if(p_svc_indx) |
| { |
| *p_svc_indx = svc_idx; |
| } |
| return p_entry; |
| } |
| } |
| } |
| } |
| |
| /* if reach the end of the list, TAILQ_FOREACH_SAFE set the p_entry to NULL */ |
| return NULL; |
| |
| } |
| |
| /* |
| * @brief The vsi list insert function |
| * |
| * @param entry the vsi element to be added in the link list |
| * @return error code |
| */ |
| static bal_sw_vsi_service *bal_sw_util_vsi_list_insert(bal_sw_vsi_service *p_entry) |
| { |
| bal_sw_vsi_service *p_new_entry; |
| |
| p_new_entry = bcmos_calloc(sizeof(bal_sw_vsi_service)); |
| if(NULL == p_new_entry) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "VSI list insert out of memory\n"); |
| return NULL; |
| } |
| *p_new_entry = *p_entry; |
| TAILQ_INSERT_TAIL(&g_swutil_vsi_list, p_new_entry, next_service); |
| return p_new_entry; |
| } |
| |
| /* |
| * @brief The vsi list remove function |
| * |
| * @param p_entry Pointer to the vsi element in the link list result from the search functions |
| * @return error code |
| */ |
| static bcmos_errno bal_sw_util_vsi_list_remove(bal_sw_vsi_service *p_entry) |
| { |
| TAILQ_REMOVE(&g_swutil_vsi_list, p_entry, next_service); |
| bcmos_free(p_entry); |
| return BCM_ERR_OK; |
| } |
| |
| /** |
| * @brief The vsi create function create a virtual switch service that contains ingress LIF |
| * and virtual switch. This service can later be connected to multiple egress LIF of multiple FLOWs. |
| * The function use the source port and vid information in the bcmbal_flow_cfg as input |
| * The pointer of the created vsi will be returned |
| * Since a vsi can provide multiple srevices, the index to the created service tag is return here |
| * |
| * @param unit switch device id |
| * @param p_flow a pointer to the flow definition which the created service will be based on |
| * @param p_svc_indx a pointer to store the created service tag index within the vsi |
| * |
| * @return pointer to the vsi service list entry, NULL if operation failed |
| */ |
| |
| bal_sw_vsi_service *bal_sw_util_dpp_vsi_service_create(int unit, bcmbal_flow_cfg *p_flow, uint32_t *p_svc_indx) |
| { |
| int rv; |
| bal_sw_lif_pkt_tag svc_pkt_tag = {0}; |
| bal_sw_vsi_service *p_vsi_service, vsi_svc_elm; |
| bcm_vlan_t vsi; |
| int multicast_id, flags; |
| |
| /* p_flow can be NULL when create vswitch for multicast group, service tag will be added when multicast flow is created */ |
| if (p_flow == NULL) |
| { |
| BCM_LOG(INFO, log_id_sw_util, "create vsi service with no service tag \n"); |
| p_vsi_service = NULL; |
| } |
| else |
| { |
| /* find out if the vsi service already exist */ |
| svc_pkt_tag.type = p_flow->data.classifier.pkt_tag_type; |
| svc_pkt_tag.o_vid = p_flow->data.classifier.o_vid; |
| svc_pkt_tag.i_vid = p_flow->data.classifier.i_vid; |
| p_vsi_service = bal_sw_util_dpp_vsi_service_get_by_tag(&svc_pkt_tag, p_svc_indx); |
| } |
| |
| /* if no service, create one */ |
| if(p_vsi_service == NULL) |
| { |
| /* initialize link list vsi element */ |
| memset(&vsi_svc_elm, 0, sizeof (bal_sw_vsi_service)); |
| /* if flow (service tag) is specified, fill in the basic info, since it is new, add it to the first tag */ |
| if(p_flow) |
| { |
| vsi_svc_elm.pkt_tag[0] = svc_pkt_tag; |
| vsi_svc_elm.num_tag = 1; |
| } |
| |
| rv = bcm_vswitch_create(unit, &vsi); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "bcm_vswitch_create failed %d \n", rv); |
| return NULL; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, " vswitch 0x%x created\n", vsi); |
| } |
| vsi_svc_elm.vswitch = vsi; |
| /* create two multicast groups (a.k.a. flooding group), one for upstream and one for downstream |
| clean up first, it is OK there is nothing to destroy */ |
| |
| /* Create the multicast group used for upstream flooding |
| * (PON-->NNI). For the upstream mcast group, the QAX hardware |
| * requires the ID to be set to a value equal to the VSI ID |
| * created above. |
| */ |
| multicast_id = vsi + BAL_DPP_US_FLOOD_OFFSET; |
| rv = bcm_multicast_group_is_free(unit, multicast_id); |
| if (rv == BCM_E_EXISTS) |
| { |
| rv = bcm_multicast_destroy(unit, multicast_id); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "US: bcm_multicast_destroy 0x%x failed %d \n", multicast_id, rv); |
| bcm_vswitch_destroy(unit, vsi); |
| return NULL; |
| } |
| } |
| /* flags = ingress replication + fixed id + L2 multicast */ |
| flags = BCM_MULTICAST_INGRESS_GROUP | BCM_MULTICAST_WITH_ID | BCM_MULTICAST_TYPE_L2; |
| rv = bcm_multicast_create(unit, flags, &multicast_id); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "US: in bcm_multicast_create 0x%x w ingress failed %d \n", multicast_id, rv); |
| bcm_vswitch_destroy(unit, vsi); |
| return NULL; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "US: vswitch flood group 0x%x created\n", multicast_id); |
| } |
| vsi_svc_elm.us_flood_grp_id = multicast_id; |
| |
| /* downstream flooding group */ |
| multicast_id = vsi + BAL_DPP_DS_FLOOD_OFFSET; |
| rv = bcm_multicast_group_is_free(unit, multicast_id); |
| if (rv == BCM_E_EXISTS) |
| { |
| rv = bcm_multicast_destroy(unit, multicast_id); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "DS: bcm_multicast_destroy 0x%x failed %d \n", multicast_id, rv); |
| bcm_multicast_destroy(unit, vsi_svc_elm.us_flood_grp_id); |
| bcm_vswitch_destroy(unit, vsi); |
| return NULL; |
| } |
| } |
| flags = BCM_MULTICAST_INGRESS_GROUP | BCM_MULTICAST_WITH_ID | BCM_MULTICAST_TYPE_L2; |
| rv = bcm_multicast_create(unit, flags, &multicast_id); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "DS: in bcm_multicast_create 0x%x w ingress 2 failed %d \n", multicast_id, rv); |
| bcm_multicast_destroy(unit, vsi_svc_elm.us_flood_grp_id); |
| bcm_vswitch_destroy(unit, vsi); |
| return NULL; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "DS: vswitch flood group 0x%x created\n", multicast_id); |
| } |
| vsi_svc_elm.ds_flood_grp_id = multicast_id; |
| |
| /* add vsi service to the vsi list */ |
| p_vsi_service = bal_sw_util_vsi_list_insert(&vsi_svc_elm); |
| if (p_vsi_service == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "VSI: fail to insert new vsi to the service list\n"); |
| bcm_multicast_destroy(unit, vsi_svc_elm.us_flood_grp_id); |
| bcm_multicast_destroy(unit, vsi_svc_elm.ds_flood_grp_id); |
| bcm_vswitch_destroy(unit, vsi_svc_elm.vswitch); |
| return NULL; |
| } |
| p_vsi_service->use_count = 1; |
| } |
| else |
| { |
| p_vsi_service->use_count++; |
| } |
| |
| return p_vsi_service; |
| } |
| |
| /** |
| * @brief The vsi destroy function free up Hardare resource of a virtual switch. |
| * It also remove the vsi fromt the service list |
| * |
| * @param unit switch device id |
| * @param p_vsi_svc a pointer to the vsi service |
| * |
| * @return error code |
| */ |
| |
| bcmos_errno bal_sw_util_dpp_vsi_service_destroy(int unit, bal_sw_vsi_service *p_vsi_svc) |
| { |
| int rv; |
| |
| /* input validation */ |
| if (p_vsi_svc == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "destroy vsi service with invalid parameters \n"); |
| return BCM_ERR_PARM; |
| } |
| |
| /* only clean up when no more users */ |
| if(p_vsi_svc->use_count > 1) |
| { |
| p_vsi_svc->use_count--; |
| return BCM_ERR_OK; |
| } |
| |
| /* free up HW resource, continue even if any failed */ |
| rv = bcm_multicast_destroy(unit, p_vsi_svc->us_flood_grp_id); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, "bcm_multicast_destroy US 0x%x failed %d \n", p_vsi_svc->us_flood_grp_id, rv); |
| } |
| rv = bcm_multicast_destroy(unit, p_vsi_svc->ds_flood_grp_id); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, "bcm_multicast_destroy DS 0x%x failed %d \n", p_vsi_svc->ds_flood_grp_id, rv); |
| } |
| rv = bcm_vswitch_destroy(unit, p_vsi_svc->vswitch); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, "bcm_multicast_destroy vswitch 0x%x failed %d \n", p_vsi_svc->vswitch, rv); |
| } |
| |
| /* remove from the service list */ |
| rv = bal_sw_util_vsi_list_remove(p_vsi_svc); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, "bcm_multicast_destroy VSI entry failed %d \n", rv); |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| |
| /** |
| * @brief The vsi port_find function search a port from the port list of a vsi service entry. |
| * |
| * @param p_vsi_svc a pointer to the vsi service |
| * @param svc_tag_indx an index to the service within the vsi that a port need to be searched |
| * @param port the ingress port that needs to be located |
| * @param idx pointer to a storage where the array index of the found port will be return |
| * |
| * @return error code |
| */ |
| |
| static bcmos_errno bal_sw_util_dpp_vsi_service_port_find(bal_sw_vsi_service *p_vsi_svc, uint32_t svc_tag_indx, uint32_t port, uint32_t *idx) |
| { |
| int i; |
| |
| /* input validation */ |
| if (p_vsi_svc == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "vsi service port find function with invalid parameters \n"); |
| return BCM_ERR_PARM; |
| } |
| /* loop through the list */ |
| for( i=0; i<p_vsi_svc->pkt_tag[svc_tag_indx].num_port; i++) |
| { |
| if(p_vsi_svc->pkt_tag[svc_tag_indx].port[i].port == port) |
| { |
| break; |
| } |
| } |
| if (i == p_vsi_svc->pkt_tag[svc_tag_indx].num_port) |
| { |
| return BCM_ERR_NOENT; |
| } |
| *idx = i; |
| return BCM_ERR_OK; |
| } |
| |
| /** |
| * @brief The vsi port_find function search a gport from the port list of a vsi service entry. |
| * |
| * @param p_vsi_svc a pointer to the vsi service |
| * @param svc_tag_indx an index to the service within the vsi that a gport need to be searched |
| * @param gport the ingress gport that needs to be located |
| * @param idx pointer to a storage where the array index of the found port will be return |
| * |
| * @return error code |
| */ |
| |
| static bcmos_errno bal_sw_util_dpp_vsi_service_gport_find(bal_sw_vsi_service *p_vsi_svc, uint32_t svc_tag_indx, uint32_t gport, uint32_t *idx) |
| { |
| int i; |
| |
| /* input validation */ |
| if (p_vsi_svc == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "vsi service port find function with invalid parameters \n"); |
| return BCM_ERR_PARM; |
| } |
| /* loop through the list */ |
| for( i=0; i<p_vsi_svc->pkt_tag[svc_tag_indx].num_port; i++) |
| { |
| if(p_vsi_svc->pkt_tag[svc_tag_indx].port[i].gport == gport) |
| { |
| break; |
| } |
| } |
| if (i == p_vsi_svc->pkt_tag[svc_tag_indx].num_port) |
| { |
| return BCM_ERR_NOENT; |
| } |
| *idx = i; |
| return BCM_ERR_OK; |
| } |
| |
| /** |
| * @brief The vsi port_add function add an ingress port to the vsi service. |
| If the port is already in the vsi, just increase the counter |
| If the port is not in the vsi, create a gport and add it to the US flooding group. |
| This allows the US SPEAK_FIRST packets to be forwarded to the network. |
| * |
| * @param unit switch device id |
| * @param p_vsi_svc a pointer to the vsi service |
| * @param svc_tag_indx an index to the service within the vsi that a port need to be added |
| * @param port the ingress port that needs to be added to the vsi service |
| * @param p_gport a valid pointer where the created/existing gport will be returned. |
| * NULL, if caller don't care the gport |
| * |
| * @return error code |
| */ |
| |
| bcmos_errno bal_sw_util_dpp_vsi_service_port_add(int unit, bal_sw_vsi_service *p_vsi_svc, uint32_t svc_tag_indx, uint32_t port, int32_t *p_gport) |
| { |
| bcm_vlan_port_t vp; |
| uint32_t idx; |
| int ind, rv; |
| int32_t gport; |
| bcmos_errno ret; |
| int port_encap_id; |
| |
| /* input validation */ |
| if (p_vsi_svc == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "vsi service port add function with invalid parameters \n"); |
| return BCM_ERR_PARM; |
| } |
| /* check if the port already in the vsi */ |
| ret = bal_sw_util_dpp_vsi_service_port_find(p_vsi_svc, svc_tag_indx, port, &idx); |
| /* if port already in the vsi, just increase the counter */ |
| if(ret == BCM_ERR_OK) |
| { |
| if(p_gport) |
| { |
| *p_gport = p_vsi_svc->pkt_tag[svc_tag_indx].port[idx].gport; |
| } |
| (p_vsi_svc->pkt_tag[svc_tag_indx].port[idx].use_count)++; |
| return BCM_ERR_OK; |
| } |
| |
| /* create the LIF */ |
| bcm_vlan_port_t_init(&vp); |
| vp.port = port; |
| /* configure frame match according to the service packet tags */ |
| switch(p_vsi_svc->pkt_tag[svc_tag_indx].type) |
| { |
| case BCMBAL_PKT_TAG_TYPE_UNTAGGED: |
| vp.criteria = BCM_VLAN_PORT_MATCH_PORT; |
| break; |
| case BCMBAL_PKT_TAG_TYPE_SINGLE_TAG: |
| vp.criteria = BCM_VLAN_PORT_MATCH_PORT_VLAN; |
| vp.match_vlan = p_vsi_svc->pkt_tag[svc_tag_indx].o_vid; |
| break; |
| case BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG: |
| vp.criteria = BCM_VLAN_PORT_MATCH_PORT_VLAN_STACKED; |
| vp.match_vlan = p_vsi_svc->pkt_tag[svc_tag_indx].o_vid; |
| vp.match_inner_vlan = p_vsi_svc->pkt_tag[svc_tag_indx].i_vid; |
| break; |
| default: |
| /* should not reach here */ |
| BCM_LOG(ERROR, log_id_sw_util, "VSI: Unsupported packet type %d \n",p_vsi_svc->pkt_tag[svc_tag_indx].type ); |
| return BCM_ERR_INTERNAL; |
| break; |
| } |
| |
| vp.vsi = 0; /* will be populated when the gport is added to service, using vswitch_port_add */ |
| vp.flags = BCM_VLAN_PORT_OUTER_VLAN_PRESERVE | BCM_VLAN_PORT_INNER_VLAN_PRESERVE; |
| |
| rv = bcm_vlan_port_create(unit, &vp); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "VSI: bcm_vlan_port_create port %d, failed %d\n", port, rv); |
| return BCM_ERR_INTERNAL; |
| } |
| |
| gport = vp.vlan_port_id; |
| |
| // add gport to vswitch |
| rv = bcm_vswitch_port_add(unit, p_vsi_svc->vswitch, gport); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "VSI, bcm_vswitch_port_add for port %d failed %d\n", port, rv); |
| bcm_vlan_port_destroy(unit, gport); |
| return BCM_ERR_INTERNAL; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "VSI: bcm_vswitch_port_add for port %d, gport 0x%x\n", port, gport); |
| } |
| /* if caller requre for the gport info, return the gport */ |
| if(p_gport) |
| { |
| *p_gport = gport; |
| } |
| /* record the gport into the vsi */ |
| ind = p_vsi_svc->pkt_tag[svc_tag_indx].num_port; |
| if (ind == MAX_NET_PORT) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "VSI, reach max port allow\n"); |
| bcm_vswitch_port_delete(unit, p_vsi_svc->vswitch, gport); |
| bcm_vlan_port_destroy(unit, gport); |
| return BCM_ERR_NORES; |
| } |
| p_vsi_svc->pkt_tag[svc_tag_indx].port[ind].port = port; |
| p_vsi_svc->pkt_tag[svc_tag_indx].port[ind].gport = gport; |
| p_vsi_svc->pkt_tag[svc_tag_indx].port[ind].use_count = 1; |
| p_vsi_svc->pkt_tag[svc_tag_indx].num_port = ++ind; |
| |
| ret = BCM_ERR_OK; |
| do |
| { |
| // update flooding group with phy_port/gport as ingress port |
| rv = bcm_multicast_vlan_encap_get(unit, p_vsi_svc->us_flood_grp_id, port, gport, &port_encap_id); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_multicast_vlan_encap_get for port failed %d\n", rv); |
| ret = BCM_ERR_NOENT; |
| break; |
| } |
| /* be a member of the upstream flooding group */ |
| rv = bcm_multicast_ingress_add(unit, p_vsi_svc->us_flood_grp_id, port, port_encap_id); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_multicast_ingress_add for port failed %d\n", rv); |
| ret = BCM_ERR_NOENT; |
| break; |
| } |
| |
| /* now set the type of packets that goes to the downstream flooding group */ |
| /* forward unknown unicast */ |
| rv = bcm_port_control_set(unit, gport, bcmPortControlFloodUnknownUcastGroup, BAL_DPP_DS_FLOOD_OFFSET); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_port_control_set ucast for nni failed %d\n", rv); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| /* drop unknown multicast */ |
| rv = bcm_port_control_set(unit, gport, bcmPortControlFloodUnknownMcastGroup, BCM_GPORT_BLACK_HOLE); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_port_control_set mcast for nni failed %d\n", rv); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| |
| /* forward broadcast */ |
| rv = bcm_port_control_set(unit, gport, bcmPortControlFloodBroadcastGroup, BAL_DPP_DS_FLOOD_OFFSET); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_port_control_set bcast for nni failed %d\n", rv); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| }while(0); |
| |
| return ret; |
| } |
| |
| /** |
| * @brief The vsi port_rem function remove an ingress port to the vsi service. |
| * |
| * @param unit switch device id |
| * @param p_vsi_svc a pointer to the vsi service |
| * @param svc_tag_indx an index to the service within the vsi that a port need to be removed |
| * @param gport the ingress gport that needs to be removed from the vsi service |
| * |
| * @return error code |
| */ |
| |
| bcmos_errno bal_sw_util_dpp_vsi_service_port_rem(int unit, bal_sw_vsi_service *p_vsi_svc, uint32_t svc_tag_indx, uint32_t gport) |
| { |
| uint32_t idx;; |
| int port_encap_id, rv, i; |
| bcmos_errno ret = BCM_ERR_OK; |
| int port; |
| /* input validation */ |
| if (p_vsi_svc == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "vsi service port rem function with invalid parameters \n"); |
| return BCM_ERR_PARM; |
| } |
| /* check if the port in the vsi */ |
| ret = bal_sw_util_dpp_vsi_service_gport_find(p_vsi_svc, svc_tag_indx, gport, &idx); |
| if(ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, vsi service port find for gport %d failed\n", gport); |
| return ret; |
| } |
| /* only remove from the array when no flow reference it */ |
| if(p_vsi_svc->pkt_tag[svc_tag_indx].port[idx].use_count > 1) |
| { |
| (p_vsi_svc->pkt_tag[svc_tag_indx].port[idx].use_count)--; |
| return BCM_ERR_OK; |
| } |
| |
| port = p_vsi_svc->pkt_tag[svc_tag_indx].port[idx].port; |
| |
| /* compact the port list */ |
| for(i=idx; i<p_vsi_svc->pkt_tag[svc_tag_indx].num_port-1; i++) |
| { |
| p_vsi_svc->pkt_tag[svc_tag_indx].port[i] = p_vsi_svc->pkt_tag[svc_tag_indx].port[i+1]; |
| } |
| memset(&p_vsi_svc->pkt_tag[svc_tag_indx].port[i], 0, sizeof (bal_sw_lif_port)); |
| |
| (p_vsi_svc->pkt_tag[svc_tag_indx].num_port)--; |
| |
| ret = BCM_ERR_OK; |
| do |
| { |
| /* find the encap_id */ |
| rv = bcm_multicast_vlan_encap_get(unit, p_vsi_svc->us_flood_grp_id, port, gport, &port_encap_id); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_multicast_vlan_encap_get for port %d failed %d\n", port, rv); |
| ret = BCM_ERR_NOENT; |
| break; |
| } |
| /* remove from the upstream flooding group */ |
| rv = bcm_multicast_ingress_delete(unit, p_vsi_svc->us_flood_grp_id, port, port_encap_id); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_multicast_ingress_delete for port %d failed %d\n", port, rv); |
| ret = BCM_ERR_NOENT; |
| break; |
| } |
| rv = bcm_vswitch_port_delete(unit, p_vsi_svc->vswitch, gport); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_vswitch_port_delete for port %d failed %d\n", port, rv); |
| ret = BCM_ERR_NOENT; |
| break; |
| } |
| rv = bcm_vlan_port_destroy(unit, gport); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, "Error, bcm_vlan_port_destroy for port %d failed %d\n", port, rv); |
| /* Likely a bug in the 6.5.4 release, igore for now |
| ret = BCM_ERR_NOENT; |
| break; |
| */ |
| } |
| }while(0); |
| return ret; |
| } |
| |
| /** |
| * @brief The vsi_service_tag_add function add the service tag of a flow to a vsi target service. |
| * |
| * @param unit switch device id |
| * @param p_vsi_target a pointer to the vsi service |
| * @param p_flow a pointer to a flow that define the service tag need to be added |
| * @param p_svc_tag_indx a pointer to store the return service tag index that just added |
| * |
| * @return error code |
| */ |
| bcmos_errno bal_sw_util_dpp_vsi_service_tag_add(int unit, bal_sw_vsi_service *p_vsi_target, bcmbal_flow_cfg *p_flow, uint32_t *p_svc_tag_indx) |
| { |
| bal_sw_lif_pkt_tag svc_pkt_tag = {0}; |
| bal_sw_vsi_service *p_vsi_service; |
| |
| /* input parameters checking */ |
| if (p_flow == NULL || p_vsi_target == NULL || p_svc_tag_indx == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "invalid parameters when adding vsi service tag \n"); |
| return BCM_ERR_PARM; |
| } |
| |
| /* find out if the service tag already exist */ |
| svc_pkt_tag.type = p_flow->data.classifier.pkt_tag_type; |
| svc_pkt_tag.o_vid = p_flow->data.classifier.o_vid; |
| svc_pkt_tag.i_vid = p_flow->data.classifier.i_vid; |
| p_vsi_service = bal_sw_util_dpp_vsi_service_get_by_tag(&svc_pkt_tag, p_svc_tag_indx); |
| if (p_vsi_service) |
| { |
| /* if the service tag already exist in the system, it has to be within the same vsi. |
| We don't allow same service tag to be serviced by more than one vsi |
| */ |
| if(p_vsi_service != p_vsi_target) |
| { |
| return BCM_ERR_INTERNAL; |
| } |
| else |
| { |
| return BCM_ERR_OK; |
| } |
| } |
| |
| /* now add the tag to the list */ |
| p_vsi_target->pkt_tag[p_vsi_target->num_tag] = svc_pkt_tag; |
| *p_svc_tag_indx = p_vsi_target->num_tag; |
| p_vsi_target->num_tag ++; |
| return BCM_ERR_OK; |
| } |
| |
| /** |
| * @brief The vsi_service_tag_rem function remove the service tag index by svc_tag_indx from a vsi service. |
| * |
| * @param unit switch device id |
| * @param p_vsi_svc a pointer to the vsi service |
| * @param svc_tag_indx a service tag index point to the service tag list that need to be removed |
| * |
| * @return error code |
| */ |
| bcmos_errno bal_sw_util_dpp_vsi_service_tag_rem(int unit, bal_sw_vsi_service *p_vsi_svc, uint32_t svc_tag_indx) |
| { |
| int i; |
| /* input parameters checking */ |
| if (p_vsi_svc == NULL || p_vsi_svc->num_tag <= svc_tag_indx) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "invalid parameters when removing vsi service tag \n"); |
| return BCM_ERR_PARM; |
| } |
| /* compact the tag list */ |
| for(i=svc_tag_indx; i<p_vsi_svc->num_tag-1; i++) |
| { |
| p_vsi_svc->pkt_tag[i] = p_vsi_svc->pkt_tag[i+1]; |
| } |
| memset(&p_vsi_svc->pkt_tag[i], 0, sizeof (bal_sw_lif_pkt_tag)); |
| |
| (p_vsi_svc->num_tag)--; |
| return BCM_ERR_OK; |
| } |
| |
| #endif /* #ifndef TEST_SW_UTIL_LOOPBACK */ |
| /*@}*/ |
| |
| |