blob: d281e1f222daa9a09a0958dd303cba824ed69639 [file] [log] [blame]
/******************************************************************************
*
* <: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 */
/*@}*/