📄 stp_appl.c
字号:
* status - 1:up 0:down
* Returns:
*/
static void
stp_send_port_info(stp_id_t tree_id, int lport, uint16 type,
uint16 status, int porttype)
{
stp_rdp_data_t *rdpd;
stp_rdp_port_t *pi;
int rdp_len;
int d_cpu;
/*
* send new port info to all other modules on the stack
*/
rdp_len = sizeof(stp_rdp_port_t) + sizeof(stp_rdp_data_t) - 1;
for (d_cpu = 1; d_cpu <= stp_stk_num_cpu; d_cpu++) {
if (d_cpu == stp_my_cpuid) {
continue;
}
rdpd = (stp_rdp_data_t *)sal_alloc(rdp_len, "stp");
rdpd->tree_id = soc_htons(tree_id);
rdpd->type = soc_htons(STP_RDP_PORT_DATA);
pi = (stp_rdp_port_t *)rdpd->value;
pi->lport = soc_htonl(lport);
pi->info_type = type;
pi->porttype = porttype;
pi->status = soc_htons(status);
rdp_send_async(d_cpu, BCM_RDP_ID_STP, (void *)rdpd, rdp_len,
STK_HI_PRIO, stp_rdp_send_callback);
}
return;
}
/*
* Function:
* stp_send_bpdu_info
* Purpose:
* Send BPDU to other CPUs in the stack
* Parameters:
* tree_id - STP instance ID
* lport - port where BPDU is received
* bpdu_buf - BPDU packet
* bpdu_len - BPDU packet len
* Returns:
*/
void
stp_send_bpdu_info(stp_id_t tree_id, uint8 *bpdu_buf, int bpdu_len, int lport)
{
stp_rdp_data_t *rdpd;
stp_bpdu_info_t *bpdu;
int rdp_len, d_cpu;
rdp_len = bpdu_len + sizeof(stp_bpdu_info_t) + sizeof(stp_rdp_data_t) - 2;
for (d_cpu = 1; d_cpu <= stp_stk_num_cpu; d_cpu++) {
if (d_cpu == stp_my_cpuid) {
continue;
}
rdpd = (stp_rdp_data_t *)sal_alloc(rdp_len, "stp");
rdpd->tree_id = soc_htons(tree_id);
rdpd->type = soc_htons(STP_RDP_BPDU_DATA);
bpdu = (stp_bpdu_info_t *)rdpd->value;
bpdu->lport = soc_htonl(lport);
sal_memcpy(bpdu->bpdu, bpdu_buf, bpdu_len);
rdp_send_async(d_cpu, BCM_RDP_ID_STP, (void *)rdpd, rdp_len,
STK_HI_PRIO, stp_rdp_send_callback);
}
}
/*
* Function:
* stp_send_timer_info
* Purpose:
* In stack mode, when timer is changed, let all CPU know
* Parameters:
* tree_id - STP instance ID
* type - Hello, max, forward delay etc
* value - New timer
* Returns:
* none
*/
void
stp_send_timer_info(stp_id_t tree_id, uint16 value, uint16 type)
{
stp_rdp_timer_t *timer_info;
stp_rdp_data_t *rdpd;
int rdp_len, d_cpu;
rdp_len = sizeof(stp_rdp_timer_t) + sizeof(stp_rdp_data_t) - 1;
/*
* send timer info to all other modules on the stack
*/
for (d_cpu = 1; d_cpu <= stp_stk_num_cpu; d_cpu++) {
if (d_cpu == stp_my_cpuid) {
continue;
}
rdpd = (stp_rdp_data_t *)sal_alloc(rdp_len, "stp");
rdpd->tree_id = soc_htons(tree_id);
rdpd->type = soc_htons(STP_RDP_TIMER_DATA);
timer_info = (stp_rdp_timer_t *)rdpd->value;
timer_info->type = soc_htons(type);
timer_info->value = soc_htons(value);
rdp_send_async(d_cpu, BCM_RDP_ID_STP, (void *)rdpd,
rdp_len, STK_HI_PRIO, stp_rdp_send_callback);
}
}
/*
* Function:
* stp_packet_bpdu_offset
* Purpose:
* decide the BPDU message offset from the start of packet
* Parameters:
* ieee_start - start of ethertype/len field of the packet
* Returns:
* the offset of the BPDU message
*/
int
stp_packet_bpdu_offset(uint8 *ieee_start)
{
e8022_form_t *e8022_hdr;
uint16 *eth_type;
int bpdu_offset = 0;
eth_type = (uint16 *)ieee_start;
if (soc_ntohs(*eth_type) >= 0x0600) { /* ethernet II */
bpdu_offset = STP_BPDU_ETH_OFFSET;
} else { /* 802.2 */
e8022_hdr = (e8022_form_t *)(ieee_start + 2);
if (e8022_hdr->dsap == 0xaa && e8022_hdr->ssap == 0xaa) {
bpdu_offset = STP_BPDU_SNAP_OFFSET;
} else if (e8022_hdr->dsap == 0xff && e8022_hdr->ssap == 0xff) {
bpdu_offset = STP_BPDU_RAW_OFFSET;
} else {
bpdu_offset = STP_BPDU_802_OFFSET;
}
}
return (bpdu_offset);
}
/*
* Initialize the STP packet queue
*/
static int
stp_pakq_int(int size)
{
int total;
total = sizeof(stp_pkt_t) * size;
if ((stpQ.pakq = sal_alloc(total, "spantree")) == NULL) {
return (BCM_E_RESOURCE);
}
sal_memset(stpQ.pakq, 0, total);
stpQ.total = size;
stpQ.count = 0;
stpQ.head = 0;
stpQ.tail = 0;
stpQ.drop = 0;
/* Setup the packet thread related stuff */
if ((stpQ.pkt_sem = sal_sem_create("stp_pak", sal_sem_BINARY, 0)) == NULL) {
return (BCM_E_RESOURCE);
}
stpQ.thread_exit = 0;
if ((stpQ.pkt_thread = sal_thread_create("stp_pkt", SAL_THREAD_STKSZ*2, 100,
stp_packet_thread, 0)) == SAL_THREAD_ERROR) {
sal_sem_destroy(stpQ.pkt_sem);
return (BCM_E_RESOURCE);
}
return (0);
}
/*
* Release the STP packet queue
*/
void
stk_pakq_release(void)
{
stpQ.thread_exit = 1;
taskDelay(100);
stpQ.count = 0;
stpQ.head = 0;
stpQ.tail = 0;
stpQ.drop = 0;
sal_free(stpQ.pakq);
stpQ.pakq = NULL;
/* stpQ.thread_exit = 1; */
sal_sem_give(stpQ.pkt_sem);
}
/*
* Function:
* stp_process_packet
* Purpose:
* DPC for handling STP packets
* Parameters:
* unit - SOC unit #
* pak - packet
* ieee_start - start of ethertype/len field
* size - length
* port - Source port where packet comes in
* Returns:
*/
void
stp_process_packet(stp_pkt_t *rx_q)
{
int stp_bpdu_offset, bpdu_size;
int tree_id, lport;
uint8 *pak = rx_q->mac_start;
lport = stp_port_p2l(stp_my_cpuid, rx_q->unit, rx_q->port);
/* Get VLAN ID, which for now is also the tree ID */
tree_id = pak[14] & 0xf;
tree_id <<= 8;
tree_id |= pak[15] & 0xff;
debugk(DK_STP,"rx pk from vlan = %d \n",tree_id);
/*
* ieee_start points to the byte right after the VLAN + SL stack tag.
* Based on the encapsulation the packet is using,
* get the offset of actuall BPDU portion from ieee_start.
*/
stp_bpdu_offset = rx_q->ieee_start - pak;
stp_bpdu_offset += stp_packet_bpdu_offset(rx_q->ieee_start);
bpdu_size = rx_q->size - stp_bpdu_offset;
/* process the BPDU message */
stp_handle_bpdu_packet(tree_id, pak + stp_bpdu_offset, lport);
/* In stack mode, send bpdu to all other modules on the stack */
if (stp_stack_ready) {
stp_send_bpdu_info(tree_id, (pak + stp_bpdu_offset), bpdu_size, lport);
}
/* release original incoming packet */
bcm_pmux_free(rx_q->unit, pak);
return;
}
#define STPQ_NEXT(idx) ((idx + 1) % stpQ.total)
#define STPQ_FULL (stpQ.count == stpQ.total)
#define STPQ_EMPTY (stpQ.count == 0)
static void
stp_packet_thread(void *param)
{
stp_pkt_t *rx_q;
int spl;
while (!stpQ.thread_exit)
{
/*
* Service as many packets as possible.
*/
while (!STPQ_EMPTY) {
spl = sal_splhi();
rx_q = &stpQ.pakq[stpQ.head];
stpQ.head = STPQ_NEXT(stpQ.head);
stpQ.count--;
sal_spl(spl);
stp_lock_link();
stp_process_packet(rx_q);
stp_unlock_link();
}
sal_sem_take(stpQ.pkt_sem, sal_sem_FOREVER);
}
/* debugk(DK_STP,"stp packet thread exit\n");*/
/* thread dies */
sal_sem_destroy(stpQ.pkt_sem);
}
/*
* Function:
* stp_sys_recv_pkts
* Purpose:
* Function called to receive BPDU packets
* Parameters:
* unit - SOC unit #
* info - information about packet
* cookie - not used
* Returns:
* bcmPmuxHandledOwned/bcmPmuxNotHandled
*/
bcm_pmux_t
stp_sys_recv_pkts(int unit, bcm_pmux_rx_t *info, void *cookie)
{
mac_addr_t bpdu_addr = {0x1, 0x80, 0xc2, 0x00, 0x00, 0x00};
stp_pkt_t *entry;
/*
* Check if packet belongs to STP based on packet DA,
* {0x1,0x80,0xc2,0x00,0x00,0x00} is the DA used in STP BPDU message
*/
if (ENET_CMP_MACADDR(info->mac_start, bpdu_addr) == 0) {
/* queue the packet */
if (STPQ_FULL) { /* Queue is full */
stpQ.drop++;
return (bcmPmuxHandled);
} else {
entry = &stpQ.pakq[stpQ.tail];
entry->unit = unit;
entry->port = info->rx_port;
entry->mac_start = info->mac_start;
entry->ieee_start = info->ieee_start;
entry->size = info->rx_len;
stpQ.count++;
stpQ.tail = STPQ_NEXT(stpQ.tail);
sal_sem_give(stpQ.pkt_sem);
return (bcmPmuxHandledOwned);
}
}
return bcmPmuxNotHandled;
}
/*
* Function:
* stp_bpdu_cos_set
* Purpose:
* Set the CONFIG reg to send BPDU packets on high COS queue
* Parameters:
* unit - SOC unit #
* Returns:
*/
int
stp_bpdu_cos_set(int unit)
{
uint32 config_reg;
uint64 cpu_ctrl;
int blk, port;
int num_cos;
/*
* Set up two COS queues
*/
SOC_IF_ERROR_RETURN(bcm_cosq_config_get(unit, &num_cos));
if (num_cos == 1) {
bcm_cosq_init(unit);
bcm_cosq_config_set(unit, 2);
bcm_cosq_mapping_set(unit, 7, (bcm_cos_queue_t)1);
} else {
bcm_cosq_mapping_set(unit, 7, (bcm_cos_queue_t)(num_cos - 1));
}
/* BPDU packets use the highest COS queue */
if (SOC_IS_STRATA(unit)) {
SOC_BLOCK_ITER(unit, blk, SOC_BLK_ETHER) {
port = SOC_BLOCK_PORT(unit, blk);
SOC_IF_ERROR_RETURN (READ_CONFIGr(unit, port, &config_reg));
soc_reg_field_set(unit, CONFIGr, &config_reg, CPU_PRIf, num_cos-1);
SOC_IF_ERROR_RETURN (WRITE_CONFIGr(unit, port, config_reg));
}
} if (SOC_IS_XGS_SWITCH(unit)) {
PBMP_E_ITER(unit, port) {
SOC_IF_ERROR_RETURN (READ_CPU_CONTROLr(unit, port, &cpu_ctrl));
soc_reg64_field32_set(
unit, CPU_CONTROLr, &cpu_ctrl, CPU_PROTOCOL_PRIORITYf, 7);
SOC_IF_ERROR_RETURN (WRITE_CPU_CONTROLr(unit, port, cpu_ctrl));
}
}
return BCM_E_NONE;
}
/*
* Given a physical port number, return the port type
*/
stp_porttype_t
stp_get_porttype(int unit, int port)
{
if (IS_FE_PORT(unit, port)) {
return STP_PORT_TYPE_FE;
}
if (IS_GE_PORT(unit, port)) {
return STP_PORT_TYPE_GE;
}
if (IS_XE_PORT(unit, port)) {
return STP_PORT_TYPE_10G; /* should be _XE */
}
if (IS_HG_PORT(unit, port)) {
return STP_PORT_TYPE_10G;
}
return STP_PORT_TYPE_FE;
}
/*
* Function:
* stp_link_scan_callback
* Purpose:
* Set up a port scan, called whenever port state changes
* Parameters:
* unit - SOC unit #
* port - port whose status changes(physical)
* info - new port info
*/
void
stp_link_scan_callback(int unit, int port, bcm_port_info_t *info)
{
spantree_t *stp;
stp_port_t *stport;
stp_id_t tree_id; /* aka vlan ID */
pbmp_t pbmp;
int lport, speed,gport;
if (bcm_stk_port_is_stack(unit, port)) {
return;
}
FOR_ALL_STP(stp) {
if (!stp_is_running(stp)) {
continue;
}
/* port belongs to this VLAN ? */
tree_id = stp->external_id;
bcm_vlan_port_get(unit, tree_id, &pbmp, NULL);
if (!PBMP_MEMBER(pbmp, port)) {
continue;
}
lport = stp_port_p2l(stp_my_cpuid, unit, port);
if (info->linkstatus) {
trace(TRACE_STP, TR_VERBOSE, "Unit %d Port %d Up\n", unit, port);
/* get the port speed */
if (bcm_port_speed_get(unit, port, &speed) < 0) {
trace(TRACE_STP, TR_ERROR, "bcm_port_speed_get err\n");
speed = 100;
}
if (stp_create_stp_port(&stport, stp, lport, "stp port") < 0) {
return;
}
stport->cpu = stp_my_cpuid;
stport->unit = unit;
stport->port = port;
stport->port_type = stp_get_porttype(unit, port);
stport->link_flags |= STP_LF_LOCAL;
/* for port id = pri + port id zhouguis adde 2005-12-22*/
gport = get_global_port(unit,port);
stport->port_id |= gport;
stp_link_up(stp, stport, speed);
if (stp_stack_ready) {
stp_send_port_info(tree_id, lport, STP_PORT_INFO_LINK,
speed, stport->port_type);
}
} else {
trace(TRACE_STP, TR_VERBOSE, "\nUnit %d Port %d down\n", unit, port);
stp_link_down(stp, lport);
if (stp_stack_ready) {
stp_send_port_info(tree_id, lport, STP_PORT_INFO_LINK,
0, stport->port_type);
}
}
}
}
/*
* Function:
* stp_port_add
* Purpose:
* Add all ports with link to spanning tree
*/
void
stp_port_add(spantree_t *stp, int stk_mode)
{
int link, lport;
int unit, pport, speed,gport;
pbmp_t pbmp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -