/** fm10000CreateForwardingRule * \ingroup intStacking * * \desc Creates a forwarding rule. * * \param[in] sw is the switch number to operate on. * * \param[in] ruleId points to caller allocated storage where the handle * of the new rule is written to. * * \param[in] rule points to caller allocated storage containing the * rule to create. * * \return FM_OK if successful. * \return FM_ERR_NO_FREE_RESOURCES if there are no hardware resources * to accommodate the new forwarding rule. * \return FM_ERR_NO_MEM if there is not enough memory to store * the forwarding rule data structure. * *****************************************************************************/ fm_status fm10000CreateForwardingRule(fm_int sw, fm_int *ruleId, fm_forwardRule *rule) { fm_status err; fm_int camIndex; fm_glortCamEntry * camEntry; fm_switch * switchPtr; fm_logicalPortInfo * lportInfo; fm_forwardRuleInternal * internalRule; fm10000_forwardRuleInternal *fwdExt; fm_stackingInfo * stackingInfo; fm_port * portPtr; FM_LOG_ENTRY( FM_LOG_CAT_STACKING, "sw=%d, ruleId=%p, rule=%p\n", sw, (void *) ruleId, (void *) rule ); switchPtr = GET_SWITCH_PTR(sw); lportInfo = &switchPtr->logicalPortInfo; portPtr = GET_PORT_PTR(sw, rule->logicalPort); fwdExt = NULL; if (portPtr == NULL) { err = FM_ERR_INVALID_PORT; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_STACKING, err); } stackingInfo = &switchPtr->stackingInfo; /*************************************************** * Find an unused CAM entry. **************************************************/ camIndex = fmFindUnusedCamEntry(sw); if (camIndex < 0) { err = FM_ERR_NO_FREE_RESOURCES; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_STACKING, err); } camEntry = &lportInfo->camEntries[camIndex]; /*************************************************** * Create an FM10000 forwarding rule extension. **************************************************/ fwdExt = (fm10000_forwardRuleInternal *) fmAlloc( sizeof(fm10000_forwardRuleInternal) ); if (fwdExt == NULL) { err = FM_ERR_NO_MEM; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_STACKING, err); } fwdExt->camEntry = camEntry; /*************************************************** * Initialize the CAM entry. **************************************************/ FM_MEMSET_S( camEntry, sizeof(fm_glortCamEntry), 0, sizeof(fm_glortCamEntry) ); camEntry->camIndex = camIndex; camEntry->camKey = rule->glort; camEntry->camMask = ~rule->mask; /* Hardware stores mask inverted from rule mask */ if ( fmIsCardinalPort(sw, rule->logicalPort) ) { camEntry->strict = FM_GLORT_ENTRY_TYPE_STRICT; } else { /* Needs to be hashed to support LAG on internal port for traffic * to be hashed across the internal LAG link. Especially * CPU traffic, since FTYPE is special delivery and won't * be hashed if set to ISL */ camEntry->strict = FM_GLORT_ENTRY_TYPE_HASHED; } /*************************************************** * This makes the assumption that the rule points * to a physical port that is not going to have its * dest table entry changing. **************************************************/ camEntry->destIndex = portPtr->destEntry->destIndex; camEntry->destCount = 1; camEntry->useCount = 1; /*************************************************** * Write CAM entry to hardware. **************************************************/ err = fm10000WriteGlortCamEntry(sw, camEntry, FM_UPDATE_CAM_AND_RAM); FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_STACKING, err); FM_LOG_DEBUG(FM_LOG_CAT_STACKING, "Wrote to CAM entry 0x%x\n", camIndex); /*************************************************** * Get the underlying forwarding rule object. **************************************************/ err = fmTreeFind( &stackingInfo->fwdRules, *ruleId, (void **) &internalRule ); FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_STACKING, err); /*************************************************** * Store pointer to extension. **************************************************/ internalRule->extension = fwdExt; err = FM_OK; ABORT: if (err != FM_OK) { if (fwdExt != NULL) { fmFree(fwdExt); } } FM_LOG_EXIT(FM_LOG_CAT_STACKING, err); } /* end fm10000CreateForwardingRule */
/** fm10000RedirectCpuTrafficToPort * \ingroup intStacking * * \desc Redirect CPU traffic to the specified logical port. * The logical port can be a physical port or a LAG. * * \note This function may be called before the port table has * been initialized. * * \param[in] sw is the switch number to operate on. * * \param[in] port is the logical port. * port value -1 indicates current cpu port configuration. * * \return FM_OK if successful. * \return FM_ERR_INVALID_PORT if port is invalid. * \return Other 'Status Codes' ad appropriate in case of failure. * *****************************************************************************/ fm_status fm10000RedirectCpuTrafficToPort(fm_int sw, fm_int port) { fm_switch * switchPtr; fm10000_switch *switchExt; fm_uint64 rv1; fm_portmask destMask; fm_status err; fm_int portList[FM_MAX_NUM_LAG_MEMBERS]; fm_int numPorts; fm_int physPort; FM_LOG_ENTRY(FM_LOG_CAT_STACKING, "sw=%d port=%d\n", sw, port); switchPtr = GET_SWITCH_PTR(sw); switchExt = GET_SWITCH_EXT(sw); FM_PORTMASK_DISABLE_ALL(&destMask); if (port < 0) { if (port == -1) { /* Use the current configuration */ port = switchPtr->cpuPort; } else { /* port number is invalid, return error */ FM_LOG_EXIT(FM_LOG_CAT_STACKING, FM_ERR_INVALID_PORT); } } else { switchPtr->cpuPort = port; } if ( fmIsCardinalPort(sw, port) ) { err = fmEnablePortInPortMask(sw, &destMask, port); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_STACKING, err); /* We have to do a formal translation here, since the port table * may not exist yet. */ err = fmMapLogicalPortToPhysical(switchPtr, port, &physPort); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_STACKING, err); rv1 = (1 << physPort); } else if ( fmIsValidLagPort(sw, port) ) { /* CPU traffic can't be filtered over internal LAG; hence, we can * only redirect the traffic to a single active member port, or * result in duplicate packets. */ err = fmGetLAGMemberPorts(sw, GET_PORT_PTR(sw, port)->lagIndex, &numPorts, portList, FM_MAX_NUM_LAG_MEMBERS, TRUE); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_STACKING, err); rv1 = 0; if (numPorts > 0) { err = fmMapLogicalPortToPhysical(switchPtr, portList[0], &physPort); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_STACKING, err); rv1 |= (FM_LITERAL_U64(1) << physPort); err = fmEnablePortInPortMask(sw, &destMask, portList[0]); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_STACKING, err); } } else { FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_STACKING, FM_ERR_INVALID_PORT); } /* Change the destination mask for cpu port so traffic * matching CPU glort will be redirected to the specified port. */ err = switchPtr->SetLogicalPortAttribute(sw, switchPtr->cpuPort, FM_LPORT_DEST_MASK, &destMask); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_STACKING, err); /* Update the CPU_TRAP_MASK so trapped frames go to the CPU. */ err = switchPtr->WriteUINT64(sw, FM10000_CPU_TRAP_MASK_FH(0), rv1); FM_LOG_EXIT(FM_LOG_CAT_STACKING, err); } /* end fm10000RedirectCpuTrafficToPort */
/** fm10000CreateLogicalPortForMailboxGlort * \ingroup intStacking * * \desc Creates a logical port for the given mailbox glort. * This occurs only if the glort is not local * to the current switch. * * \param[in] sw is the switch number to operate on. * * \param[in] glort is the glort for which a logical port is to be created * * \param[out] logicalPort is a pointer to an integer into which the * new logical port will be written. * * \return FM_OK if successful. * \return FM_ERR_GLORT_IN_USE if the specified is already being used * by others. * \return FM_ERR_NO_FREE_RESOURCES if logical port numbers available * for allocation. * \return FM_ERR_NO_MEM if no memory available to store logical * port data structure. * *****************************************************************************/ fm_status fm10000CreateLogicalPortForMailboxGlort(fm_int sw, fm_uint32 glort, fm_int * logicalPort) { fm_status err; fm_switch * switchPtr; fm_stackingInfo *stackingInfo; fm_port * portPtr; fm10000_port * portExt; fm_bool found; FM_LOG_ENTRY( FM_LOG_CAT_STACKING, "sw=%d, glort=%d, logicalPort=%p\n", sw, glort, (void *) logicalPort ); switchPtr = GET_SWITCH_PTR(sw); stackingInfo = &switchPtr->stackingInfo; found = FALSE; /* If there is an existing entry already, then return the existing one */ err = fmGetGlortLogicalPort(sw, glort, logicalPort); if (err == FM_OK) { /* The port already exists, see if it is the same type */ portPtr = GET_PORT_PTR(sw, *logicalPort); if (portPtr->portType == FM_PORT_TYPE_REMOTE) { /* Yes, return the existing port */ FM_LOG_EXIT(FM_LOG_CAT_STACKING, FM_OK); } else { /* The glort is being used for something else */ FM_LOG_EXIT(FM_LOG_CAT_STACKING, FM_ERR_GLORT_IN_USE); } } err = FM_OK; /*************************************************** * Find an unused logical port number. **************************************************/ *logicalPort = fmFindUnusedLogicalPorts(sw, 1); if (*logicalPort == -1) { FM_LOG_EXIT(FM_LOG_CAT_STACKING, FM_ERR_NO_FREE_RESOURCES); } /*************************************************** * Create an entry for the remote logical port. **************************************************/ portPtr = (fm_port *) fmAlloc(sizeof(fm_port)); if (portPtr == NULL) { FM_LOG_EXIT(FM_LOG_CAT_STACKING, FM_ERR_NO_MEM); } FM_MEMSET_S( portPtr, sizeof(fm_port), 0, sizeof(fm_port) ); portExt = (fm10000_port *) fmAlloc( sizeof(fm10000_port) ); if (portExt == NULL) { fmFree(portPtr); FM_LOG_EXIT(FM_LOG_CAT_STACKING, FM_ERR_NO_MEM); } FM_MEMSET_S( portExt, sizeof(fm10000_port), 0, sizeof(fm10000_port) ); portPtr->switchPtr = switchPtr; portPtr->portNumber = *logicalPort; portPtr->portType = FM_PORT_TYPE_REMOTE; portPtr->extension = portExt; portPtr->lagIndex = -1; portPtr->memberIndex = -1; portPtr->glort = glort; portPtr->swagPort = -1; /* Remote port structures created for mailbox purpose does not have CAM entries assigned, as these entries are not created dynamically. Mailbox CAM entries are created at the mailbox init stage to optimise GLORT_CAM/GLORT_RAM entries usage. */ portPtr->camEntry = NULL; /* Initialize multicast group membership list */ fmTreeInit(&portPtr->mcastGroupList); /*************************************************** * Add it to the logical port table. **************************************************/ switchPtr->portTable[*logicalPort] = portPtr; FM_LOG_EXIT(FM_LOG_CAT_STACKING, err); } /* end fm10000CreateLogicalPortForMailboxGlort */
/** fm10000CreateLogicalPortForGlort * \ingroup intStacking * * \desc Creates a logical port for the given glort. This occurs * only if the glort is not local to the current switch and * a forwarding rule exists for the glort, otherwise an * error is returned. * * \param[in] sw is the switch number to operate on. * * \param[in] glort is the glort for which a logical port is to be created * * \param[out] logicalPort is a pointer to an integer into which the * new logical port will be written. * * \return FM_OK if successful. * \return FM_ERR_GLORT_IN_USE if the specified is already being used * by others. * \return FM_ERR_NO_FORWARDING_RULES if no forwarding rule associated * with glort. * \return FM_ERR_NO_FREE_RESOURCES if logical port numbers available * for allocation. * \return FM_ERR_NO_MEM if no memory available to store logical * port data structure. * *****************************************************************************/ fm_status fm10000CreateLogicalPortForGlort(fm_int sw, fm_uint32 glort, fm_int * logicalPort) { fm_status err; fm_switch * switchPtr; fm_stackingInfo * stackingInfo; fm_port * portPtr; fm_glortCamEntry * camEntry; fm10000_port * portExt; fm_treeIterator iter; fm_forwardRuleInternal *tmpRule; fm_uint64 tmpId; fm_bool found; FM_LOG_ENTRY( FM_LOG_CAT_STACKING, "sw=%d, glort=%d, logicalPort=%p\n", sw, glort, (void *) logicalPort ); switchPtr = GET_SWITCH_PTR(sw); stackingInfo = &switchPtr->stackingInfo; found = FALSE; /* If there is an existing entry already, then return the existing one */ err = fmGetGlortLogicalPort(sw, glort, logicalPort); if (err == FM_OK) { /* The port already exists, see if it is the same type */ portPtr = GET_PORT_PTR(sw, *logicalPort); if (portPtr->portType == FM_PORT_TYPE_REMOTE) { /* Yes, return the existing port */ FM_LOG_EXIT(FM_LOG_CAT_STACKING, FM_OK); } else { /* The glort is being used for something else */ FM_LOG_EXIT(FM_LOG_CAT_STACKING, FM_ERR_GLORT_IN_USE); } } /*************************************************** * Search the tree for a forwarding rule with a * matching glort. **************************************************/ fmTreeIterInit(&iter, &stackingInfo->fwdRules); err = fmTreeIterNext( &iter, &tmpId, (void **) &tmpRule ); while (err != FM_ERR_NO_MORE) { if ( ( ~tmpRule->rule.mask & tmpRule->rule.glort ) == ( ~tmpRule->rule.mask & glort ) ) { found = TRUE; break; } err = fmTreeIterNext( &iter, &tmpId, (void **) &tmpRule ); } if ( !found ) { FM_LOG_DEBUG(FM_LOG_CAT_STACKING, "Glort 0x%x was not matched to a forwarding rule\n", glort); FM_LOG_EXIT(FM_LOG_CAT_STACKING, FM_ERR_NO_FORWARDING_RULES); } FM_LOG_DEBUG(FM_LOG_CAT_STACKING, "Glort 0x%x was matched to forwarding rule #%lld\n", glort, tmpId); camEntry = ( (fm10000_forwardRuleInternal *) tmpRule->extension )->camEntry; /*************************************************** * Find an unused logical port number. **************************************************/ *logicalPort = fmFindUnusedLogicalPorts(sw, 1); if (*logicalPort == -1) { FM_LOG_EXIT(FM_LOG_CAT_STACKING, FM_ERR_NO_FREE_RESOURCES); } /*************************************************** * Create an entry for the remote logical port. **************************************************/ portPtr = (fm_port *) fmAlloc(sizeof(fm_port)); if (portPtr == NULL) { FM_LOG_EXIT(FM_LOG_CAT_STACKING, FM_ERR_NO_MEM); } FM_MEMSET_S( portPtr, sizeof(fm_port), 0, sizeof(fm_port) ); portExt = (fm10000_port *) fmAlloc( sizeof(fm10000_port) ); if (portExt == NULL) { fmFree(portPtr); FM_LOG_EXIT(FM_LOG_CAT_STACKING, FM_ERR_NO_MEM); } FM_MEMSET_S( portExt, sizeof(fm10000_port), 0, sizeof(fm10000_port) ); portPtr->switchPtr = switchPtr; portPtr->portNumber = *logicalPort; portPtr->portType = FM_PORT_TYPE_REMOTE; portPtr->extension = portExt; portPtr->lagIndex = -1; portPtr->memberIndex = -1; portPtr->glort = glort; portPtr->swagPort = -1; portPtr->camEntry = camEntry; /* Increase the CAM reference use count */ portPtr->camEntry->useCount++; portPtr->destEntry = NULL; portPtr->numDestEntries = 0; /* Initialize multicast group membership list */ fmTreeInit(&portPtr->mcastGroupList); /* Set the default to down, the application must * bring it up, similar to physical port. */ portPtr->mode = FM_PORT_STATE_DOWN; /*************************************************** * Add it to the logical port table. **************************************************/ switchPtr->portTable[*logicalPort] = portPtr; FM_LOG_EXIT(FM_LOG_CAT_STACKING, err); } /* end fm10000CreateLogicalPortForGlort */
/** MoveListenersToReplicationGroup * \ingroup intMulticast * * \desc Move a group of listeners to a replication group. * * \param[in] sw is the switch number. * * \param[in] repliGroup is the replication group number. * * \param[in] mcastGroup points to the multicast group entry, * * \return FM_OK if successful. * \return FM_ERR_INVALID_PORT if a listener port is invalid * *****************************************************************************/ static fm_status MoveListenersToReplicationGroup(fm_int sw, fm_int repliGroup, fm_intMulticastGroup *mcastGroup) { fm_status err; fm_switch * switchPtr; fm_int mcastLogicalPort; fm_intMulticastListener *intListener; fm_int listenerCount; fm_int i; fm_treeIterator iter; fm_uint64 key; fm10000_mtableEntry listener; fm_port *portPtr; fm_int port; FM_LOG_ENTRY( FM_LOG_CAT_MULTICAST, "sw=%d repliGroup=%d mcastLogPort=%d\n", sw, repliGroup, mcastGroup->logicalPort); switchPtr = GET_SWITCH_PTR(sw); err = FM_OK; listenerCount = 0; mcastLogicalPort = mcastGroup->logicalPort; /* Get the number of listeners to move */ listenerCount = (fm_int) fmTreeSize(&mcastGroup->listenerTree); if (listenerCount > 0) { i = 0; fmTreeIterInit(&iter, &mcastGroup->listenerTree); while (1) { err = fmTreeIterNext(&iter, &key, (void **) &intListener); if (err != FM_OK) { if (err != FM_ERR_NO_MORE) { FM_LOG_ABORT(FM_LOG_CAT_MULTICAST, err); } err = FM_OK; break; } if (intListener->listener.listenerType != FM_MCAST_GROUP_LISTENER_PORT_VLAN) { continue; } if (i++ >= listenerCount) { err = FM_FAIL; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_MULTICAST, err); } listener.vlan = intListener->listener.info.portVlanListener.vlan; listener.vlanUpdate = TRUE; if ( (intListener->listener.info.portVlanListener.vlan == 0) && (intListener->listener.info.portVlanListener.port == -1) ) { port = switchPtr->cpuPort; portPtr = NULL; } else { port = intListener->listener.info.portVlanListener.port; portPtr = GET_PORT_PTR(sw, port); if (!portPtr) { FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_MULTICAST, FM_ERR_INVALID_PORT); } } if ( (portPtr != NULL) && (portPtr->portType == FM_PORT_TYPE_VIRTUAL) ) { err = fm10000MapVirtualGlortToLogicalPort(sw, portPtr->glort, &listener.port); FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_MULTICAST, err); listener.dglortUpdate = TRUE; listener.dglort = portPtr->glort; if (intListener->listener.info.portVlanListener.vlan == 0) { listener.vlanUpdate = FALSE; } } else { listener.port = port; listener.dglortUpdate = FALSE; listener.dglort = 0; } err = fm10000MTableAddListener(sw, mcastLogicalPort, repliGroup, listener); FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_MULTICAST, err); } /* Sanity check */ if (i != listenerCount) { err = FM_FAIL; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_MULTICAST, err); } } ABORT: FM_LOG_EXIT(FM_LOG_CAT_MULTICAST, err); } /* end MoveListenersToReplicationGroup */
/** MapLagPortToMemberPort * \ingroup intSwitch * * \desc Maps a LAG logical port to one of its member ports. * * \param[in] sw is the switch to operate on. * * \param[in] port is the LAG logical port number. * * \param[out] logPort points to the location to receive the logical * port number. * * \return FM_OK if successful. * \return FM_ERR_INVALID_PORT if the port number is invalid. * \return FM_ERR_NO_PORTS_IN_LAG if port refers to an empty LAG. * *****************************************************************************/ static fm_status MapLagPortToMemberPort(fm_int sw, fm_int port, fm_int *logPort) { fm_int portList[FM_MAX_NUM_LAG_MEMBERS]; fm_port * portPtr; fm_lag * lagPtr; fm_int numPorts; fm_status err; portPtr = GET_PORT_PTR(sw, port); if (portPtr == NULL) { return FM_ERR_INVALID_PORT; } lagPtr = GET_LAG_PTR(sw, portPtr->lagIndex); if(lagPtr == NULL) { return FM_ERR_INVALID_PORT; } /* Get a list of active member ports. */ err = fmGetLAGCardinalPortList(sw, lagPtr->logicalPort, &numPorts, portList, FM_MAX_NUM_LAG_MEMBERS); if (err != FM_OK) { return err; } if (numPorts == 0) { /* Return the first port even if the link is currently down. * This should ensure that a SWAG or Stacked Configuration * works properly at init time. */ err = fmGetLAGMemberPorts(sw, portPtr->lagIndex, &numPorts, portList, FM_MAX_NUM_LAG_MEMBERS, FALSE); if (err != FM_OK) { return err; } else if (numPorts == 0) { return FM_ERR_NO_PORTS_IN_LAG; } } /* * Select one of the LAG ports using pseudo-random selection. * * Why are we doing "pseudo-random" selection? Why not (for * example) return the logical port that corresponds to the canonical * logical port? */ *logPort = portList[port % numPorts]; return FM_OK; } /* end MapLagPortToMemberPort */