/** fmPlatformLoadPropertiesFromLine * \ingroup intPlatform * * \desc Loads an attribute from a text line * \lb\lb * The expected file format for the database is as follows: * \lb\lb * [key] [type] [value] * \lb\lb * Where key is a dotted string, type is one of int, bool or * float, and value is the value to set. Space is the only * valid separator, but multiple spaces are allowed. * * \param[in] line is the text line to load to load. * * \return FM_OK if successful. * *****************************************************************************/ fm_status fmPlatformLoadPropertiesFromLine(fm_text line) { fm_status status; FM_LOG_ENTRY(FM_LOG_CAT_PLATFORM, "line=%s\n", line); if (strncmp(line, "api.platform.config.", 20) == 0) { status = LtCfgLineLoad(line); } else if (strncmp(line, "api.platform.lib.config.", 24) == 0) { status = LibCfgLineLoad(line); } else { status = ApiPropertyLineLoad(line); } if (status) { FM_LOG_ERROR(FM_LOG_CAT_PLATFORM, "%s: Unable to load config: [%s]\n", fmErrorMsg(status), line); } FM_LOG_EXIT(FM_LOG_CAT_PLATFORM, FM_OK); } /* end fmPlatformLoadPropertiesFromLine */
/** InitiatePurge * \ingroup intAddr * * \desc Initiates an MA table purge operation. * * \note The caller is assumed to have taken the purge lock * (FM_TAKE_MA_PURGE_LOCK). * * \param[in] sw is the switch on which to operate. * * \return FM_OK if successful. * *****************************************************************************/ static fm_status InitiatePurge(fm_int sw) { fm_switch * switchPtr; fm_maPurge * purgePtr; fm_maPurgeRequest * request; fm_status err; switchPtr = GET_SWITCH_PTR(sw); purgePtr = &switchPtr->maPurge; request = &purgePtr->request; FM_LOG_DEBUG(FM_LOG_CAT_EVENT_MAC_MAINT, "sw=%d, port=%d, vlan=%d\n", sw, request->port, request->vid1); purgePtr->restoreLocked = FALSE; purgePtr->purgeTimeout = 0; /* Get purge start time. */ err = fmGetTime( &purgePtr->startTime ); if (err != FM_OK) { FM_LOG_ERROR(FM_LOG_CAT_EVENT_MAC_MAINT, "Error getting purge start time: %s\n", fmErrorMsg(err)); } purgePtr->purgeState = FM_PURGE_STATE_ACTIVE; return FM_OK; } /* end InitiatePurge */
/* LtCfgLineLoad * \ingroup intPlatform * * \desc Load LT text configuration line. * \lb\lb * \param[in] line is the config line to load. * * \return FM_OK if successful. * \return Other ''Status Codes'' as appropriate in case of * failure. * *****************************************************************************/ static fm_status LtCfgLineLoad(fm_text line) { fm_status status; fm_byte tlv[FM_TLV_MAX_BUF_SIZE]; fm_uint tlvType; /* Don't support these properties here, only for NVM image generation */ if (strstr(line, ".bootCfg.") != NULL) { return FM_OK; } status = fmUtilConfigPropertyEncodeTlv(line, tlv, sizeof(tlv)); if (status) { FM_LOG_ERROR(FM_LOG_CAT_PLATFORM, "Unable to encode LT config: [%s]\n", line); return status; } tlvType = (tlv[0] << 8) | tlv[1]; if (tlvType == FM_TLV_PLAT_FILE_LOCK_NAME) { /* Shared with platform config */ status = LibCfgLineLoad(line); if (status) { return status; } } return fmPlatformLoadLTCfgTlv(tlv); } /* end LtCfgLineLoad */
/** fmRawPacketSocketDestroy * \ingroup intPlatformCommon * * \desc Destroy the raw packet socket and free up resource. * * \param[in] sw is the switch number to destroy. * * \return FM_OK if successful. * *****************************************************************************/ fm_status fmRawPacketSocketDestroy(fm_int sw) { fm_status err = FM_OK; fm_switch *switchPtr; FM_LOG_ENTRY(FM_LOG_CAT_PLATFORM, "sw=%d\n", sw); if (close(GET_PLAT_STATE(sw)->rawSocket) == -1) { FM_LOG_ERROR(FM_LOG_CAT_PLATFORM, "couldn't not destroy raw packet socket\n"); err = FM_FAIL; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_PLATFORM, err); } switchPtr = GET_SWITCH_PTR(sw); if (switchPtr) { switchPtr->isRawSocketInitialized = FM_DISABLED; } err = fmGenericPacketDestroy(sw); FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_PLATFORM, err); ABORT: FM_LOG_EXIT(FM_LOG_CAT_PLATFORM, err); } /* end fmRawPacketSocketDestroy */
/** fmLocalEventHandler * \ingroup intSwitch * * \desc event handler for local dispatch thread * * \param[in] args points to the thread arguments * * \return Nothing. * *****************************************************************************/ void *fmLocalEventHandler(void *args) { fm_thread *thread; fm_event * event; fm_status status; fm_bool enablePrioritySchedule; /* grab arguments */ thread = FM_GET_THREAD_HANDLE(args); enablePrioritySchedule = GET_PROPERTY()->priorityBufQueues; while (1) { if (fmGetThreadEvent(thread, &event, FM_WAIT_FOREVER) != FM_OK) { if (localDispatchThreadExit == TRUE) { break; } else { continue; } } if (enableFramePriority && ( (event->type == FM_EVENT_PKT_RECV) || (event->type == FM_EVENT_SFLOW_PKT_RECV) ) ) { status = fmFreeBufferQueueNode(FM_FIRST_FOCALPOINT, &event->info.fpPktEvent); if (status != FM_OK) { FM_LOG_ERROR(FM_LOG_CAT_EVENT_PKT_RX, "Freeing Buffer queue node from the queue failed" "status = %d (%s) \n", status, fmErrorMsg(status)); fmReleaseEvent(event); continue; } } if (fmEventHandler != NULL) { fmEventHandler(event->type, event->sw, &event->info); } fmReleaseEvent(event); } fmExitThread(thread); return NULL; } /* end fmLocalEventHandler */
/** fmSetProcessEventMask * \ingroup api * * \desc Set which events are delivered to the current process's * event handler function (set by ''fmInitialize'' or * ''fmSetEventHandler''). * \lb\lb * Multiple processes may subscribe to the same event. A * copy of each event will be sent to all processes that * subscribe to it. * * \param[in] mask is a logical OR of ''Event Identifiers''. * * \return FM_OK if successful. * *****************************************************************************/ fm_status fmSetProcessEventMask(fm_uint32 mask) { fm_status err; fm_dlist_node * node; fm_uint count = 0; fm_localDelivery *delivery; fm_int myProcessId; fm_uint expectedCount; FM_LOG_ENTRY_API(FM_LOG_CAT_API, "mask=%u\n", mask); myProcessId = fmGetCurrentProcessId(); err = fmCaptureLock(&fmRootApi->localDeliveryLock, FM_WAIT_FOREVER); if (err == FM_OK) { expectedCount = fmRootApi->localDeliveryCount; for ( node = FM_DLL_GET_FIRST( (&fmRootApi->localDeliveryThreads), head ) ; node != NULL ; node = FM_DLL_GET_NEXT(node, next) ) { count++; delivery = (fm_localDelivery *) node->data; if (delivery->processId == myProcessId) { delivery->mask = mask; } } err = fmReleaseLock(&fmRootApi->localDeliveryLock); if (count != expectedCount) { FM_LOG_ERROR(FM_LOG_CAT_EVENT, "Expected %d processes but found %d\n", expectedCount, count); err = FM_FAIL; } } /* end if (err == FM_OK) */ FM_LOG_EXIT_API(FM_LOG_CAT_API, err); } /* end fmSetProcessEventMask */
/* LibCfgLineLoad * \ingroup intPlatform * * \desc Save shared lib text configuration line for the * shared lib to load when it initializes. * \lb\lb * \param[in] line is the config line to load. * * \return FM_OK if successful. * \return Other ''Status Codes'' as appropriate in case of * failure. * *****************************************************************************/ static fm_status LibCfgLineLoad(fm_text line) { fm_status status; fm_byte tlv[FM_TLV_MAX_BUF_SIZE]; status = fmUtilConfigPropertyEncodeTlv(line, tlv, sizeof(tlv)); if (status) { FM_LOG_ERROR(FM_LOG_CAT_PLATFORM, "Unable to encode LT config: [%s]\n", line); return status; } return fmPlatformLoadLibCfgTlv(tlv); } /* end LibCfgLineLoad */
/* ApiPropertyLineLoad * \ingroup intPlatform * * \desc Load API text configuration line. * \lb\lb * \param[in] line is the config line to load. * * \return FM_OK if successful. * \return Other ''Status Codes'' as appropriate in case of * failure. * *****************************************************************************/ static fm_status ApiPropertyLineLoad(fm_text line) { fm_status status; fm_byte tlv[FM_TLV_MAX_BUF_SIZE]; status = fmUtilConfigPropertyEncodeTlv(line, tlv, sizeof(tlv)); if (status) { FM_LOG_ERROR(FM_LOG_CAT_PLATFORM, "Unable to encode API property: [%s]\n", line); return status; } return fmLoadApiPropertyTlv(tlv); } /* end ApiPropertyLineLoad */
/* _fmPlatformFm10000I2cWriteRead * \ingroup intPlatformfm10000I2c * * \desc Write to then immediately read from an I2C device with * handling max response bytes using switch as I2C master. * * \param[in] handle is the memory pointer to the switch. * * \param[in] device is the I2C device address (0x00 - 0x7F). * * \param[in,out] data points to an array from which data is written and * into which data is read. * * \param[in] wl is the number of bytes to write. * * \param[in] rl is the number of bytes to read. * * \return FM_OK if successful. * \return Other ''Status Codes'' as appropriate in case of * failure. * *****************************************************************************/ static fm_status _fmPlatformFm10000I2cWriteRead(fm_uintptr handle, fm_uint device, fm_byte *data, fm_uint wl, fm_uint rl) { fm_status status; fm_uint32 regValue; fm_uint i; fm_uint j; fm_bool isTimeout; struct timeval startTime; fm_uint32 *memPtr; fm_uint32 tmp; if (i2cDebug & 0x4) { FM_LOG_PRINT("fmPlatformfm10000I2cWriteRead " "handle=%p device=0x%x wl=%d rl=%d\n", (void*)handle, device, wl, rl); } memPtr = (fm_uint32*)handle; if (rl > I2C_MAX_LEN) { FM_LOG_EXIT(FM_LOG_CAT_PLATFORM, FM_ERR_INVALID_ARGUMENT); } if (wl > I2C_MAX_LEN) { FM_LOG_EXIT(FM_LOG_CAT_PLATFORM, FM_ERR_INVALID_ARGUMENT); } if (data == NULL) { FM_LOG_EXIT(FM_LOG_CAT_PLATFORM, FM_ERR_INVALID_ARGUMENT); } for (i = 0 ; i < (wl+3)/4 ; i++) { regValue = 0; for (j = 0 ; j < 4 ; j++) { if ((i*4 + j) < wl) { regValue |= (data[i*4 + j] << ((3 - j)*8)); } } status = WriteSwitchMemMap(memPtr, FM10000_I2C_DATA(i), regValue); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_PLATFORM, status); if (i2cDebug & 0x4) { FM_LOG_PRINT("WRITEDATA#%d : 0x%08x\n",i, regValue); } } regValue = 0; FM_SET_FIELD(regValue, FM10000_I2C_CTRL, Addr, (device << 1)); FM_SET_FIELD(regValue, FM10000_I2C_CTRL, Command, 0); FM_SET_FIELD(regValue, FM10000_I2C_CTRL, LengthW, wl); FM_SET_FIELD(regValue, FM10000_I2C_CTRL, LengthR, rl); if (i2cDebug & 0x4) { FM_LOG_PRINT("WRITE: eg 0x%08x Cmd %d wl %d rl %d " "Comp %x LenSent %d intr %d\n", regValue, FM_GET_FIELD(regValue, FM10000_I2C_CTRL, Command), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthW), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthR), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthSent), FM_GET_BIT(regValue, FM10000_I2C_CTRL, InterruptPending)); } status = WriteSwitchMemMap(memPtr, FM10000_I2C_CTRL, regValue); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_PLATFORM, status); status = ReadSwitchMemMap(memPtr, FM10000_I2C_CTRL, &tmp); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_PLATFORM, status); if (i2cDebug & 0x4) { FM_LOG_PRINT("READ: %d reg 0x%08x Cmd %d wl %d rl %d " "Comp %x LenSent %d intr %d\n", status, tmp, FM_GET_FIELD(tmp, FM10000_I2C_CTRL, Command), FM_GET_FIELD(tmp, FM10000_I2C_CTRL, LengthW), FM_GET_FIELD(tmp, FM10000_I2C_CTRL, LengthR), FM_GET_FIELD(tmp, FM10000_I2C_CTRL, CommandCompleted), FM_GET_FIELD(tmp, FM10000_I2C_CTRL, LengthSent), FM_GET_BIT(tmp, FM10000_I2C_CTRL, InterruptPending)); } /* Now change command to start the transaction */ if (rl == 0) { /* Write only allow write of 0 length */ FM_SET_FIELD(regValue, FM10000_I2C_CTRL, Command, 1); } else if ((wl > 0) && (rl > 0)) { /* Write Read */ FM_SET_FIELD(regValue, FM10000_I2C_CTRL, Command, 2); } else { /* Read */ FM_SET_FIELD(regValue, FM10000_I2C_CTRL, Command, 3); } if (i2cDebug & 0x4) { FM_LOG_PRINT("WRITE2: reg 0x%08x Cmd %d wl %d rl %d " "Comp %x LenSent %d intr %d\n", regValue, FM_GET_FIELD(regValue, FM10000_I2C_CTRL, Command), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthW), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthR), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthSent), FM_GET_BIT(regValue, FM10000_I2C_CTRL, InterruptPending)); } status = WriteSwitchMemMap(memPtr, FM10000_I2C_CTRL, regValue); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_PLATFORM, status); status = ReadSwitchMemMap(memPtr, FM10000_I2C_CTRL, ®Value); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_PLATFORM, status); if (i2cDebug & 0x4) { FM_LOG_PRINT("READ2: %d reg 0x%08x Cmd %d wl %d rl %d " "Comp %x LenSent %d intr %d\n", status, regValue, FM_GET_FIELD(regValue, FM10000_I2C_CTRL, Command), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthW), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthR), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthSent), FM_GET_BIT(regValue, FM10000_I2C_CTRL, InterruptPending)); } /* Poll to check for command done */ gettimeofday(&startTime, NULL); isTimeout = FALSE; do { usleep(10000); if (isTimeout) { FM_LOG_ERROR( FM_LOG_CAT_PLATFORM, "Dev=0x%02x: Timeout (%d msec) waiting " "for I2C_CTRL(0x%x).CommandCompleted!=0. 0x%02x\n", device, I2C_TIMEOUT, FM10000_I2C_CTRL, FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted)); FM_LOG_EXIT(FM_LOG_CAT_PLATFORM, FM_ERR_I2C_NO_RESPONSE); } /* Variable isTimeout is used to improve the timeout detecting */ if (GetTimeIntervalMsec(&startTime, NULL) > I2C_TIMEOUT) { isTimeout = TRUE; } status = ReadSwitchMemMap(memPtr, FM10000_I2C_CTRL, ®Value); if (i2cDebug & 0x4) { FM_LOG_PRINT( "STATUS: %d reg 0x%08x cmd %d wl %d rl %d " "Comp %x LenSent %d intr %d\n", status, regValue, FM_GET_FIELD(regValue, FM10000_I2C_CTRL, Command), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthW), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthR), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthSent), FM_GET_BIT(regValue, FM10000_I2C_CTRL, InterruptPending)); } if (status) break; } while (FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted) == 0); if (FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted) != 1) { if (FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted) == 3) { if (i2cDebug & 0x4) { FM_LOG_PRINT("No ACK received for address 0x%02x\n", device); } } else { FM_LOG_ERROR(FM_LOG_CAT_PLATFORM, "Dev=0x%02x: I2C Command completed with error 0x%x. " "I2C_CTRL(0x%x)=0x%x\n", device, FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted), FM10000_I2C_CTRL, regValue); } FM_LOG_EXIT(FM_LOG_CAT_PLATFORM, FM_ERR_I2C_NO_RESPONSE); } for (i = 0 ; i < (rl+3)/4 ; i++) { status = ReadSwitchMemMap(memPtr, FM10000_I2C_DATA(i), ®Value); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_PLATFORM, status); if (i2cDebug & 0x4) { FM_LOG_PRINT("READDATA#%d : 0x%08x\n",i, regValue); } for (j = 0 ; j < 4 ; j++) { if ((i*4 + j) < rl) { data[i*4 + j] = (regValue >> ((3 - j)*8)) & 0xFF; } } }
/** fmParityRepairTask * \ingroup intParity * * \chips FM10000 * * \desc Generic thread wrapper for chip specific parity * repair handler. * * \param[in] args contains a pointer to the thread information. * * \return Should never exit. * *****************************************************************************/ void * fmParityRepairTask(void * args) { fm_thread * thread; fm_switch * switchPtr; fm_thread * eventHandler; fm_status err; fm_int sw; fm_bool switchProtected = FALSE; thread = FM_GET_THREAD_HANDLE(args); eventHandler = FM_GET_THREAD_PARAM(fm_thread, args); /* If logging is disabled, thread and eventHandler won't be used */ FM_NOT_USED(thread); FM_NOT_USED(eventHandler); FM_LOG_ENTRY(FM_LOG_CAT_SWITCH, "thread=%s, eventHandler=%s\n", thread->name, eventHandler->name); /************************************************** * Loop forever. **************************************************/ for (;;) { /* Wait for something to do. */ err = fmWaitSemaphore(&fmRootApi->parityRepairSemaphore, FM_WAIT_FOREVER); if (err != FM_OK && err != FM_ERR_SEM_TIMEOUT) { FM_LOG_ERROR(FM_LOG_CAT_PARITY, "Unexpected error from fmWaitSemaphore: %s\n", fmErrorMsg(err)); continue; } /* Process each active switch in turn. */ for (sw = FM_FIRST_FOCALPOINT ; sw <= FM_LAST_FOCALPOINT ; sw++) { if (!SWITCH_LOCK_EXISTS(sw)) { continue; } PROTECT_SWITCH(sw); switchProtected = TRUE; switchPtr = GET_SWITCH_PTR(sw); if ( switchPtr && (switchPtr->state == FM_SWITCH_STATE_UP) && switchPtr->parityRepairEnabled && switchPtr->ParityRepairTask ) { switchPtr->ParityRepairTask(sw, &switchProtected, args); } if (switchProtected) { UNPROTECT_SWITCH(sw); } } fmYield(); } /* for (;;) */ /************************************************** * Should never exit. **************************************************/ FM_LOG_ERROR(FM_LOG_CAT_PARITY, "fmParityRepairTask exiting inadvertently!\n"); fmExitThread(thread); return NULL; } /* end fmParityRepairTask */
/** ProcessSample * \ingroup intFastMaint * * \desc Processes a register sample from the MA_USED_TABLE. * * \param[in] sw is the switch on which to operate * * \param[in] index is the index of the first entry in the * MA_USED_TABLE to be read. * * \param[in] numWords is the number of entries in the MA_USED_TABLE * to be read. * * \param[in] currentTime is the current value of the aging timer. * * \param[in] agingTime is the length of time required for an entry * to age from YOUNG to OLD. * * \param[in] expiryTime is the length of time required for an entry * to age out. * * \param[in,out] stats points to a structure containing a number of * counters that will be incremented to show the number * of MA Table entries that are updated. * * \return FM_OK if successful. * *****************************************************************************/ static fm_status ProcessSample(fm_int sw, fm_int index, fm_int numWords, fm_uint64 currentTime, fm_uint64 agingTime, fm_uint64 expiryTime, fm_sweepStats * stats) { fm_internalMacAddrEntry * cachePtr; fm_switch * switchPtr; fm_uint32 used[numWords]; fm_status status; fm_int entryIndex; fm_int i; fm_int j; fm_uint64 elapsedTime; fm_sweepStats sampleStats; switchPtr = GET_SWITCH_PTR(sw); FM_CLEAR(sampleStats); FM_TAKE_L2_LOCK(sw); /* Read next sample from MA_USED_TABLE. */ status = switchPtr->ReadUINT32Mult(sw, FM10000_MA_USED_TABLE(1, index), numWords, used); if (status != FM_OK) { FM_DROP_L2_LOCK(sw); FM_LOG_ERROR(FM_LOG_CAT_EVENT_FAST_MAINT, "Error reading MA_USED_TABLE: %s\n", fmErrorMsg(status)); goto ABORT; } /* Clear any hits we detected. */ status = switchPtr->WriteUINT32Mult(sw, FM10000_MA_USED_TABLE(1, index), numWords, used); if (status != FM_OK) { FM_DROP_L2_LOCK(sw); FM_LOG_ERROR(FM_LOG_CAT_EVENT_FAST_MAINT, "Error writing MA_USED_TABLE: %s\n", fmErrorMsg(status)); goto ABORT; } /* Process each word in the sample. */ for (i = 0 ; i < numWords ; ++i) { /* Process each bit in the word. */ for (j = 0 ; j < ENTRIES_PER_WORD ; ++j) { /* Get the MA Table entry corresponding to this bit. */ entryIndex = (index + i) * ENTRIES_PER_WORD + j; cachePtr = &switchPtr->maTable[entryIndex]; /* We only care about dynamic entries that are eligible * for aging. If it's not one of these, keep going. */ if (cachePtr->state != FM_MAC_ENTRY_STATE_OLD && cachePtr->state != FM_MAC_ENTRY_STATE_YOUNG) { continue; } /* If the entry is USED, set its state to YOUNG * and restart its aging timer. */ if (used[i] & (1 << j)) { cachePtr->state = FM_MAC_ENTRY_STATE_YOUNG; cachePtr->agingCounter = currentTime; ++sampleStats.young; continue; } /* Get the age of this entry. */ elapsedTime = currentTime - cachePtr->agingCounter; if (elapsedTime >= expiryTime) { /* The entry has aged out. */ cachePtr->state = FM_MAC_ENTRY_STATE_EXPIRED; ++sampleStats.expired; FM_LOG_DEBUG(FM_LOG_CAT_EVENT_FAST_MAINT, "expired: index=%d mac=%012llx vid=%u " "elapsed=%llu\n", entryIndex, cachePtr->macAddress, cachePtr->vlanID, elapsedTime); } else if (cachePtr->state == FM_MAC_ENTRY_STATE_YOUNG && elapsedTime >= agingTime) { /* The entry has gone from YOUNG to OLD. */ cachePtr->state = FM_MAC_ENTRY_STATE_OLD; ++sampleStats.old; FM_LOG_DEBUG(FM_LOG_CAT_EVENT_FAST_MAINT, "aged: index=%d mac=%012llx vid=%u " "elapsed=%llu\n", entryIndex, cachePtr->macAddress, cachePtr->vlanID, elapsedTime); } } /* end for (j = 0 ; j < ENTRIES_PER_WORD ; ++j) */ } /* for (i = 0 ; i < numWords ; ++i) */ FM_DROP_L2_LOCK(sw); ABORT: stats->young += sampleStats.young; stats->old += sampleStats.old; stats->expired += sampleStats.expired; return status; } /* end ProcessSample */
/** fmOpenDynamicLoadLibs * \ingroup alosDynLoadLib * * \desc Open a Dynamic Load Library. * * \param[in] filePath points to the string containing the library path. * * \param[out] handle points to caller-provided memory into which the * dynamic-load-library's handle will be written. * * \return FM_ERR_UNINITIALIZED if the ALOS subsystem has not been * properly initialized. * \return FM_ERR_INVALID_ARGUMENT if one of the arguments is invalid. * \return FM_OK if successful. * \return FM_ERR_NO_MEM if unable to allocate memory. * \return FM_ERR_TABLE_FULL if the dynamic-load library table is full. * \return FM_ERR_NOT_FOUND if the dynamic-load library open failed. * *****************************************************************************/ fm_status fmOpenDynamicLoadLibrary(fm_text filePath, fm_int *handle) { fm_status err; fm_int index; fm_dynLoadLib *lib; fm_int availIndex; fm_int pathLen; fm_bool lockTaken; fm_bool libAllocated; void * libHandle; FM_LOG_ENTRY( FM_LOG_CAT_ALOS_DLLIB, "filePath = %p (%s), handle = %p\n", (void *) filePath, (filePath != NULL) ? filePath : "<NULL>", (void *) handle ); lib = NULL; availIndex = -1; lockTaken = FALSE; libAllocated = FALSE; if (fmRootAlos == NULL) { FM_LOG_EXIT(FM_LOG_CAT_ALOS_DLLIB, FM_ERR_UNINITIALIZED); } if (filePath == NULL) { FM_LOG_EXIT(FM_LOG_CAT_ALOS_DLLIB, FM_ERR_INVALID_ARGUMENT); } if (handle == NULL) { FM_LOG_EXIT(FM_LOG_CAT_ALOS_DLLIB, FM_ERR_INVALID_ARGUMENT); } pathLen = strlen(filePath); if (pathLen <= 0) { FM_LOG_EXIT(FM_LOG_CAT_ALOS_DLLIB, FM_ERR_INVALID_ARGUMENT); } err = fmCaptureLock(&fmRootAlos->dlAccessLock, FM_WAIT_FOREVER); FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_ALOS_DLLIB, err); lockTaken = TRUE; for (index = 0 ; index < FM_ALOS_INTERNAL_DYN_LOAD_LIBS ; index++) { if (fmRootAlos->dlLibs[index] == NULL) { if (availIndex < 0) { availIndex = index; } } else { lib = fmRootAlos->dlLibs[index]; if ( strcmp(filePath, lib->filePath) == 0 ) { break; } } } if (index >= FM_ALOS_INTERNAL_DYN_LOAD_LIBS) { index = availIndex; } if (index < 0) { err = FM_ERR_TABLE_FULL; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_ALOS_DLLIB, err); } lib = fmRootAlos->dlLibs[index]; if (lib == NULL) { lib = fmAlloc( sizeof(fm_dynLoadLib) ); if (lib == NULL) { err = FM_ERR_NO_MEM; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_ALOS_DLLIB, err); } libAllocated = TRUE; FM_CLEAR(*lib); lib->filePath = fmAlloc( pathLen + 1 ); if (lib->filePath == NULL) { err = FM_ERR_NO_MEM; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_ALOS_DLLIB, err); } FM_STRNCPY_S(lib->filePath, pathLen + 1, filePath, pathLen + 1 ); lib->useCount = 0; fmRootAlos->dlLibs[index] = lib; } if ( ( fmProcessDynLoadLibStatus & (1 << index) ) == 0 ) { libHandle = dlopen(filePath, RTLD_NOW | RTLD_GLOBAL); if (libHandle == NULL) { char *errMsg = dlerror(); FM_LOG_ERROR(FM_LOG_CAT_ALOS_DLLIB, "Error opening library %s: %s\n", filePath, errMsg); err = FM_ERR_NOT_FOUND; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_ALOS_DLLIB, err); } ProcessHandles[index] = libHandle; lib->useCount++; fmProcessDynLoadLibStatus |= FM_LITERAL_U64(1) << index; } *handle = index; err = FM_OK; ABORT: if ( (err != FM_OK) && libAllocated ) { if (lib->filePath != NULL) { fmFree(lib->filePath); } fmFree(lib); } if (lockTaken) { fmReleaseLock(&fmRootAlos->dlAccessLock); } FM_LOG_EXIT(FM_LOG_CAT_ALOS_DLLIB, err); } /* end fmOpenDynamicLoadLibrary */
/** fmRawPacketSocketSendPackets * \ingroup intPlatformCommon * * \desc When called, iterates through the packet queue and * continues to send packets until either the queue empties. * * \param[in] sw refers to the switch number to send packets to. * * \return FM_OK if successful. * *****************************************************************************/ fm_status fmRawPacketSocketSendPackets(fm_int sw) { fm_status err = FM_OK; fm_switch * switchPtr; fm_packetHandlingState *pktState; fm_packetQueue * txQueue; fm_packetEntry * packet; fm_int32 rc; fm_buffer *sendBuf; struct msghdr msg; struct iovec iov[UIO_MAXIOV]; fm_islTag islTag; fm_uint32 fcs; fm_uint64 rawTS; char strErrBuf[FM_STRERROR_BUF_SIZE]; errno_t strErrNum; struct ifreq ifr; FM_LOG_ENTRY(FM_LOG_CAT_EVENT_PKT_TX, "sw = %d\n", sw); switchPtr = GET_SWITCH_PTR(sw); pktState = GET_PLAT_PKT_STATE(sw); if (GET_PLAT_STATE(sw)->rawSocket <= 0) { FM_LOG_ERROR(FM_LOG_CAT_EVENT_PKT_TX, "Socket is not initialized.\n"); FM_LOG_EXIT(FM_LOG_CAT_EVENT_PKT_TX, FM_ERR_UNINITIALIZED); } /* initialize the message header */ FM_CLEAR(msg); msg.msg_name = NULL; /* Optional field */ msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 0; msg.msg_flags = 0; FM_STRNCPY_S(ifr.ifr_name, IF_NAMESIZE, GET_PLAT_STATE(sw)->ifaceName, IF_NAMESIZE); txQueue = &pktState->txQueue; fmPacketQueueLock(txQueue); if (ioctl(GET_PLAT_STATE(sw)->rawSocket, SIOCGIFFLAGS, &ifr) == -1) { strErrNum = FM_STRERROR_S(strErrBuf, FM_STRERROR_BUF_SIZE, errno); if (strErrNum == 0) { FM_LOG_FATAL(FM_LOG_CAT_EVENT_PKT_TX, "Failed to get socket %d flags for device %s: %s\n", GET_PLAT_STATE(sw)->rawSocket, ifr.ifr_name, strErrBuf); } else { FM_LOG_FATAL(FM_LOG_CAT_EVENT_PKT_TX, "Failed to get socket %d flags for device %s: %d\n", GET_PLAT_STATE(sw)->rawSocket, ifr.ifr_name, errno); } switchPtr->transmitterLock = TRUE; err = FM_FAIL; FM_LOG_ABORT(FM_LOG_CAT_EVENT_PKT_TX, err); } if ((ifr.ifr_flags & IFF_RUNNING) == 0) { FM_LOG_WARNING(FM_LOG_CAT_EVENT_PKT_TX, "Network device %s resources are not allocated.\n", ifr.ifr_name); switchPtr->transmitterLock = TRUE; err = FM_FAIL; FM_LOG_ABORT(FM_LOG_CAT_EVENT_PKT_TX, err); } /* Iterate through the packets in the tx queue */ for ( ; txQueue->pullIndex != txQueue->pushIndex ; txQueue->pullIndex = (txQueue->pullIndex + 1) % FM_PACKET_QUEUE_SIZE) { packet = &txQueue->packetQueueList[txQueue->pullIndex]; FM_LOG_DEBUG(FM_LOG_CAT_EVENT_PKT_TX, "sending packet in slot %d, length=%d tag=%d fcs=%08x\n", txQueue->pullIndex, packet->length, packet->suppressVlanTag, packet->fcsVal); msg.msg_iovlen = 0; /* Add the 8 byte timetag iovec. Note that the value is ignored * by the driver as it gets overwritten by the PEP. */ rawTS = 0; iov[msg.msg_iovlen].iov_base = &rawTS; iov[msg.msg_iovlen].iov_len = sizeof(rawTS); msg.msg_iovlen++; if (packet->islTagFormat == FM_ISL_TAG_F56) { /* Add the FTAG (F56) iovec */ islTag.f56.tag[0] = htonl(packet->islTag.f56.tag[0]); islTag.f56.tag[1] = htonl(packet->islTag.f56.tag[1]); iov[msg.msg_iovlen].iov_base = &islTag.f56.tag[0]; iov[msg.msg_iovlen].iov_len = FM_F56_BYTE_LEN; msg.msg_iovlen++; } /* iterate through all buffers */ for ( sendBuf = packet->packet ; sendBuf ; sendBuf = sendBuf->next ) { /* if first buffer ... */ if (sendBuf == packet->packet) { /* Cannot modify the send buffer, since the same buffer can be * used multiple times to send to multiple ports */ /* second iovec is the mac header */ iov[msg.msg_iovlen].iov_base = sendBuf->data; iov[msg.msg_iovlen].iov_len = FM_MAC_HDR_BYTE_LEN; msg.msg_iovlen++; if (packet->islTagFormat == FM_ISL_TAG_F64) { /* Insert the F64 ISL tag */ islTag.f64.tag[0] = htonl(packet->islTag.f64.tag[0]); islTag.f64.tag[1] = htonl(packet->islTag.f64.tag[1]); iov[msg.msg_iovlen].iov_base = &islTag.f64.tag[0]; iov[msg.msg_iovlen].iov_len = FM_F64_BYTE_LEN; msg.msg_iovlen++; } /* Third is the data in the first chain */ if (packet->suppressVlanTag) { iov[msg.msg_iovlen].iov_base = &sendBuf->data[4]; iov[msg.msg_iovlen].iov_len = sendBuf->len-16; msg.msg_iovlen++; } else { iov[msg.msg_iovlen].iov_base = &sendBuf->data[3]; iov[msg.msg_iovlen].iov_len = sendBuf->len-12; msg.msg_iovlen++; } } else { /* The rest of the chain */ iov[msg.msg_iovlen].iov_base = sendBuf->data; iov[msg.msg_iovlen].iov_len = sendBuf->len; msg.msg_iovlen++; } } /* end for (...) */ /* Append user-supplied FCS value to packet. */ if (pktState->sendUserFcs) { fcs = htonl(packet->fcsVal); iov[msg.msg_iovlen].iov_base = &fcs; iov[msg.msg_iovlen].iov_len = sizeof(fcs); msg.msg_iovlen++; } /* now send it to the driver */ errno = 0; rc = sendmsg(GET_PLAT_STATE(sw)->rawSocket, &msg, MSG_DONTWAIT); if (rc == -1) { switchPtr->transmitterLock = TRUE; if (errno != EWOULDBLOCK) { err = FM_FAIL; FM_LOG_DEBUG(FM_LOG_CAT_EVENT_PKT_TX, "rawSocket %d\n", GET_PLAT_STATE(sw)->rawSocket); strErrNum = FM_STRERROR_S(strErrBuf, FM_STRERROR_BUF_SIZE, errno); if (strErrNum == 0) { FM_LOG_ERROR(FM_LOG_CAT_EVENT_PKT_TX, "sendmsg failed: %s - errno %d\n", strErrBuf, errno); } else { FM_LOG_ERROR(FM_LOG_CAT_EVENT_PKT_TX, "sendmsg failed - errno %d\n", errno); } } if (errno == EMSGSIZE) { switchPtr->transmitterLock = FALSE; } else { goto ABORT; } } else { FM_LOG_DEBUG(FM_LOG_CAT_EVENT_PKT_TX, "%d bytes were sent\n", rc); switchPtr->transmitterLock = FALSE; fmDbgDiagCountIncr(sw, FM_CTR_TX_PKT_COMPLETE, 1); } /************************************************** * free buffer only when * (1) sending to a single port; * or (2) this is the last packet of multiple * identical packets **************************************************/ if (packet->freePacketBuffer) { /* ignore the error code since it's better to continue */ (void) fmFreeBufferChain(sw, packet->packet); fmDbgGlobalDiagCountIncr(FM_GLOBAL_CTR_TX_BUFFER_FREES, 1); } } ABORT: fmPacketQueueUnlock(txQueue); FM_LOG_EXIT(FM_LOG_CAT_EVENT_PKT_TX, err); } /* end fmRawPacketSocketSendPackets */
/** fmReceivePacketTask * \ingroup intApi * * \desc Handles reception of packets. * * \param[in] args contains a pointer to the thread information. * * \return FM_OK if successful. * *****************************************************************************/ void *fmReceivePacketTask(void *args) { fm_thread *thread; fm_status err = FM_OK; fm_int sw; thread = FM_GET_THREAD_HANDLE(args); FM_LOG_ENTRY(FM_LOG_CAT_SWITCH, "thread = %s\n", thread->name); /************************************************** * Loop forever, waiting for signals from the * interrupt handler. **************************************************/ while (TRUE) { /************************************************** * Wait for a signal from the interrupt handler. **************************************************/ FM_LOG_DEBUG(FM_LOG_CAT_EVENT_PKT_RX, "fmLCIReceivePacketTask: waiting for signal..\n"); err = fmWaitSemaphore(&fmRootApi->packetReceiveSemaphore, FM_WAIT_FOREVER); if (err != FM_OK) { FM_LOG_ERROR( FM_LOG_CAT_SWITCH, "%s: %s\n", thread->name, fmErrorMsg(err) ); continue; } FM_LOG_DEBUG(FM_LOG_CAT_EVENT_PKT_RX, "fmReceivePacketTask: signaled!\n"); for (sw = FM_FIRST_FOCALPOINT ; sw <= FM_LAST_FOCALPOINT ; sw++) { if ( (fmRootApi->fmSwitchStateTable[sw] != NULL) && (!fmRootApi->isSwitchFibmSlave[sw]) ) { fmReceivePacket(sw); } } } /* end while (TRUE) */ /************************************************** * Should never exit. **************************************************/ FM_LOG_ERROR(FM_LOG_CAT_SWITCH, "ERROR: fmReceivePacketTask: exiting inadvertently!\n"); return NULL; } /* end fmReceivePacketTask */
/** fmDistributeEvent * \ingroup intSwitch * * \desc distributes events to those processes that have registered * an interest in the particular event. * * \param[in] event points to the event structure. * * \return Nothing. * *****************************************************************************/ void fmDistributeEvent(fm_event *event) { fmCaptureLock(&fmRootApi->localDeliveryLock, FM_WAIT_FOREVER); { /************************************************** * We want to have a consistent snapshot of the * local delivery list, but we don't want to hold * the lock while we deliver all the events, so we * briefly grab the lock and copy the list into a * C99 variable-size array. **************************************************/ fm_uint count = fmRootApi->localDeliveryCount; fm_localDelivery delivery[count]; fm_dlist_node * node; fm_uint i; fm_uint pktDeliveryCount = 0; fm_eventPktRecv *rcvPktEvent = NULL; fm_status status; fm_buffer *buffer; node = FM_DLL_GET_FIRST( (&fmRootApi->localDeliveryThreads), head ); for (i = 0 ; (node != NULL) && (i < count) ; i++) { delivery[i] = *(fm_localDelivery *) node->data; if ( (delivery[i].mask & (FM_EVENT_PKT_RECV | FM_EVENT_SFLOW_PKT_RECV)) & event->type ) { /* Found thread we need to deliver packet to. */ pktDeliveryCount++; } node = FM_DLL_GET_NEXT(node, next); } /************************************************** * If the event is packet receive but no one has * registered for the event, free the associated * packet buffer and return. **************************************************/ if ( ( (event->type == FM_EVENT_PKT_RECV) || (event->type == FM_EVENT_SFLOW_PKT_RECV) ) && (pktDeliveryCount == 0) ) { rcvPktEvent = &event->info.fpPktEvent; if (enableFramePriority) { status = fmFreeBufferQueueNode(event->sw, rcvPktEvent); if (status != FM_OK) { FM_LOG_ERROR(FM_LOG_CAT_EVENT_PKT_RX, "Freeing Buffer queue node from the queue failed" "status = %d (%s) \n", status, fmErrorMsg(status)); } } fmFreeBufferChain(event->sw, rcvPktEvent->pkt); fmDbgDiagCountIncr(event->sw, FM_CTR_RX_API_PKT_DROPS, 1); fmReleaseLock(&fmRootApi->localDeliveryLock); return; } /* valid actually found */ count = i; fmReleaseLock(&fmRootApi->localDeliveryLock); /************************************************** * Now we do the actual delivery **************************************************/ for (i = 0 ; i < count ; i++) { fm_event *localEvent = NULL; fm_uint64 nanos = MIN_WAIT_NANOS; fm_status err = FM_FAIL; fm_uint32 numUpdates; if ( (delivery[i].mask & event->type) == 0 ) { continue; } /************************************************** * Always use high priority for the locally dispatched * events, because DistributeEvent is only called from * a single thread (the global event handler), and if * we allocated both low and high priority events here, * we could get priority inversion. **************************************************/ while (localEvent == NULL) { localEvent = fmAllocateEvent(event->sw, event->eventID, event->type, FM_EVENT_PRIORITY_HIGH); if (localEvent == NULL) { DELAY_NANOS(nanos); nanos *= 2; if (nanos > MAX_WAIT_NANOS) { nanos = MAX_WAIT_NANOS; FM_LOG_WARNING(FM_LOG_CAT_EVENT, "Waiting to allocate event of type %d " "for switch %d\n", event->type, event->sw); } } } if (event->type == FM_EVENT_TABLE_UPDATE) { /************************************************** * Because the updates field is a pointer to memory * that has been "secretly" allocated after the event, * rather than just being part of the union, we have * to handle it specially. **************************************************/ numUpdates = event->info.fpUpdateEvent.numUpdates; localEvent->info.fpUpdateEvent.numUpdates = numUpdates; FM_MEMCPY_S( localEvent->info.fpUpdateEvent.updates, numUpdates * sizeof(fm_eventTableUpdate), event->info.fpUpdateEvent.updates, numUpdates * sizeof(fm_eventTableUpdate) ); } else if (event->type == FM_EVENT_PURGE_SCAN_COMPLETE) { localEvent->info.purgeScanComplete = event->info.purgeScanComplete; } else if ( (event->type == FM_EVENT_PKT_RECV) || (event->type == FM_EVENT_SFLOW_PKT_RECV) ) { rcvPktEvent = &event->info.fpPktEvent; /************************************************** * Copy the whole event, including the packet, to * localEvent. If this is not the last registered * client, we will overwrite the packet with a * clone. **************************************************/ localEvent->info = event->info; /************************************************** * If there is more than one remaining process that is * interested in receive packet events, clone the * receive buffer for delivery. **************************************************/ if (pktDeliveryCount-- > 1) { if (enableFramePriority) { FM_LOG_ERROR(FM_LOG_CAT_EVENT, "Prioritization is supported only for the" "first registered process. Subsequent " "processes follow normal buffer allocation" " without prioritization.\n"); } localEvent->info.fpPktEvent.pkt = fmDuplicateBufferChain(event->sw, rcvPktEvent->pkt); if (localEvent->info.fpPktEvent.pkt == NULL) { /************************************************** * Couldn't copy the packet. Free the event so that * it is not lost and continue the loop. **************************************************/ fmReleaseEvent(localEvent); fmDbgDiagCountIncr(event->sw, FM_CTR_RX_API_PKT_DROPS, 1); continue; } } if (enableFramePriority) { buffer = ((fm_buffer *)(localEvent->info.fpPktEvent.pkt)); buffer->recvEvent = localEvent; } } else { /************************************************** * Otherwise, we can just copy the whole union * without worrying what type it is. **************************************************/ localEvent->info = event->info; } /************************************************** * Now try to send the event to the local dispatch * thread, using exponential backoff if the event * queue is full. **************************************************/ nanos = MIN_WAIT_NANOS; while (err != FM_OK) { err = fmSendThreadEvent(delivery[i].thread, localEvent); if (err != FM_OK) { DELAY_NANOS(nanos); nanos *= 2; if (nanos > MAX_WAIT_NANOS) { nanos = MAX_WAIT_NANOS; } } /* end if (err != FM_OK) */ } /* end while (err != FM_OK) */ } /* end for (i = 0 ; i < count ; i++) */ } /* end (local scope) */ } /* end fmDistributeEvent */
/** fmGlobalEventHandler * \ingroup intSwitch * * \desc event handler for handling system events * * \param[in] args points to the thread arguments * * \return Nothing. * *****************************************************************************/ void *fmGlobalEventHandler(void *args) { fm_thread * thread; fm_event * event; fm_status err = FM_OK; fm_eventPort * portEvent = NULL; fm_eventTableUpdateBurst *updateEvent; fm_int physPort; fm_int logicalPort; fm_eventPktRecv * rcvPktEvent; fm_eventSwitchInserted * insertEvent; fm_eventSwitchRemoved * removeEvent; fm_int sw = 0; fm_bool discardEvent; fm_port * portPtr = NULL; fm_bool switchIsProtected; fm_switch * switchPtr; fm_int mode; fm_int info[8]; fm_int state; fm_int numLanes; fm_uint32 i; fm_bool isPhysicalSwitch; fm_switchEventHandler eventHandler; fm_bool distributeEvent; fm_eventTableUpdate * fpUpdateEvent; /* grab arguments */ thread = FM_GET_THREAD_HANDLE(args); /* wait for initialization to finish before processing events */ fmCaptureSemaphore(&fmRootApi->startGlobalEventHandler, FM_WAIT_FOREVER); enableFramePriority = GET_PROPERTY()->priorityBufQueues; while (1) { /* wait forever for an event */ err = fmGetThreadEvent(thread, &event, FM_WAIT_FOREVER); if (err == FM_ERR_NO_EVENTS_AVAILABLE) { /* A timeout occurred - should never happen. */ continue; } if (event == NULL) { /* NULL event should never happen. */ continue; } sw = event->sw; discardEvent = FALSE; switchIsProtected = FALSE; switchPtr = NULL; if (sw < 0 || sw >= fmRootPlatform->cfg.numSwitches) { discardEvent = TRUE; switchIsProtected = FALSE; } else if ( SWITCH_LOCK_EXISTS(sw) ) { if ( ( err = PROTECT_SWITCH(sw) ) != FM_OK ) { discardEvent = TRUE; switchIsProtected = FALSE; } else { switchIsProtected = TRUE; switchPtr = fmRootApi->fmSwitchStateTable[sw]; if (!fmRootApi->fmSwitchStateTable[sw]) { if ((event->type != FM_EVENT_SWITCH_REMOVED) && (event->type != FM_EVENT_SWITCH_INSERTED) ) { discardEvent = TRUE; } } else if (fmRootApi->fmSwitchStateTable[sw]->state != FM_SWITCH_STATE_UP) { if ((event->type != FM_EVENT_SWITCH_REMOVED) && (event->type != FM_EVENT_SWITCH_INSERTED) ) { discardEvent = TRUE; } } } } else if (event->type != FM_EVENT_SWITCH_INSERTED) { discardEvent = TRUE; } if (discardEvent) { switch (event->type) { case FM_EVENT_PKT_RECV: case FM_EVENT_SFLOW_PKT_RECV: /* Only dig into the event if the switch is valid */ if ( (sw >= 0) && (sw < fmRootPlatform->cfg.numSwitches) ) { rcvPktEvent = &event->info.fpPktEvent; if (enableFramePriority) { err = fmFreeBufferQueueNode(sw, rcvPktEvent); if (err != FM_OK) { FM_LOG_ERROR(FM_LOG_CAT_EVENT_PKT_RX, "Freeing Buffer queue node from the queue failed" "status = %d (%s) \n", err, fmErrorMsg(err)); } } fmFreeBufferChain(sw, rcvPktEvent->pkt); fmDbgDiagCountIncr(sw, FM_CTR_RX_API_PKT_DROPS, 1); } break; default: break; } goto FINISHED; } eventHandler = NULL; if (switchPtr != NULL) { /* If the switch state table has an eventHandler pointer, * it overrides the global handler. Call the switch-specific * function to handle the event. This is intended to be used * for switches in a switch aggregate (and potentially * nested switch aggregates inside other switch aggregates?). */ eventHandler = switchPtr->eventHandler; switch (switchPtr->switchModel) { case FM_SWITCH_MODEL_SWAG: isPhysicalSwitch = FALSE; break; default: isPhysicalSwitch = TRUE; break; } } else { /* Only physical switches should ever get here with a NULL pointer * because logical switches such as switch aggregates are always * created by application code before any events related to * the switch are possible. */ isPhysicalSwitch = TRUE; } distributeEvent = FALSE; switch (event->type) { case FM_EVENT_SWITCH_INSERTED: insertEvent = &event->info.fpSwitchInsertedEvent; if (switchIsProtected) { UNPROTECT_SWITCH(sw); switchIsProtected = FALSE; } if (switchPtr == NULL) { if (fmHandleSwitchInserted(sw, insertEvent) != FM_OK) { /* Don't generate an insert event if there error */ goto FINISHED; } } distributeEvent = TRUE; break; case FM_EVENT_SWITCH_REMOVED: removeEvent = &event->info.fpSwitchRemovedEvent; if (switchIsProtected) { UNPROTECT_SWITCH(sw); switchIsProtected = FALSE; } if (switchPtr != NULL) { fmHandleSwitchRemoved(sw, removeEvent); } distributeEvent = TRUE; break; case FM_EVENT_PORT: portEvent = &event->info.fpPortEvent; if (isPhysicalSwitch && portEvent->activeMac) { logicalPort = portEvent->port; if (switchPtr != NULL) { fmMapLogicalPortToPhysical(switchPtr, logicalPort, &physPort); portPtr = switchPtr->portTable[logicalPort]; } else { portPtr = NULL; } if (portPtr == NULL) { FM_LOG_ERROR(FM_LOG_CAT_EVENT_PORT, "Unexpected NULL port pointer for logical" " port %d\n", logicalPort); break; } /* This attribute indicate whether the API should flush * all the addresses on a port down event or not. */ if (GET_PROPERTY()->maFlushOnPortDown) { /* If a link goes down for a non-LAG port, remove any * addresses associated with the port from the MA Table. */ if (portEvent->linkStatus == FM_PORT_STATUS_LINK_DOWN) { if (portPtr->portType != FM_PORT_TYPE_LAG) { err = fmFlushPortAddresses(sw, portEvent->port); if (err != FM_OK) { FM_LOG_WARNING(FM_LOG_CAT_EVENT_PORT, "%s\n", fmErrorMsg(err)); } } } } FM_LOG_DEBUG( FM_LOG_CAT_EVENT_PORT, "Port %s event reported on port %d.\n", (portPtr != NULL ) ? ( (portPtr->linkUp) ? "UP " : "DOWN" ) : "UNKN", portEvent->port ); /* inform LAG module of port state changes */ if (portEvent->linkStatus == FM_PORT_STATUS_LINK_UP) { fmInformLAGPortUp(sw, portEvent->port); /* Inform LBGs of port link state change. */ FM_API_CALL_FAMILY_VOID(switchPtr->InformLBGLinkChange, sw, portEvent->port, FM_PORT_STATUS_LINK_UP); } else if (portEvent->linkStatus == FM_PORT_STATUS_LINK_DOWN) { fmInformLAGPortDown(sw, portEvent->port); /* Inform LBGs of port link state change. */ FM_API_CALL_FAMILY_VOID(switchPtr->InformLBGLinkChange, sw, portEvent->port, FM_PORT_STATUS_LINK_DOWN); } /* now update all the source masks */ fmUpdateSwitchPortMasks(sw); if (switchPtr->UpdateRemoveDownPortsTrigger != NULL) { /**************************************************** * Update the switchExt->removeDownPortsTrigger * used to drop routed/multicast/special delivery * frames which can not be handled by the PORT_CFG_2. * See Bugzilla 11387. ***************************************************/ if (portEvent->linkStatus == FM_PORT_STATUS_LINK_UP) { switchPtr->UpdateRemoveDownPortsTrigger(sw, physPort, FALSE); } else if (portEvent->linkStatus == FM_PORT_STATUS_LINK_DOWN) { if (!portPtr->isPortForceUp) { switchPtr->UpdateRemoveDownPortsTrigger(sw, physPort, TRUE); } } } if (switchPtr->UpdateMirrorGroups != NULL) { /**************************************************** * Enable/Disable mirror groups based on the link * status of the mirror port. * See Bugzilla 11387. ***************************************************/ if (portEvent->linkStatus == FM_PORT_STATUS_LINK_UP) { switchPtr->UpdateMirrorGroups(sw, logicalPort, TRUE); } else if (portEvent->linkStatus == FM_PORT_STATUS_LINK_DOWN) { switchPtr->UpdateMirrorGroups(sw, logicalPort, FALSE); } } /* notify anyone else who needs to know */ if (portPtr && portPtr->NotifyLinkEvent) { err = portPtr->NotifyLinkEvent(sw, portEvent->port); if (err != FM_OK) { FM_LOG_WARNING(FM_LOG_CAT_EVENT_PORT, "%s\n", fmErrorMsg(err)); } } /* Get port state and notify platform */ err = fmGetPortStateV3( sw, portEvent->port, portEvent->mac, 8, &numLanes, &mode, &state, info ); if (err != FM_OK) { FM_LOG_WARNING(FM_LOG_CAT_EVENT_PORT, "fmGetPortState(%d,%u) failed: %s\n", sw, portEvent->port, fmErrorMsg(err)); } fmPlatformNotifyPortState(sw, portEvent->port, portEvent->mac, FALSE, state); if (switchIsProtected) { UNPROTECT_SWITCH(sw); switchIsProtected = FALSE; } } /* end if (isPhysicalSwitch) */ distributeEvent = TRUE; break; case FM_EVENT_PKT_RECV: case FM_EVENT_SFLOW_PKT_RECV: fmDbgDiagCountIncr(sw, FM_CTR_RX_API_PKT_FWD, 1); distributeEvent = TRUE; break; case FM_EVENT_PURGE_SCAN_COMPLETE: distributeEvent = TRUE; break; case FM_EVENT_TABLE_UPDATE: if (switchPtr == NULL) { break; } /* Update diagnostic counters. */ updateEvent = &event->info.fpUpdateEvent; i = 0; while (i < updateEvent->numUpdates) { fpUpdateEvent = &updateEvent->updates[i]; if (fpUpdateEvent->event == FM_EVENT_ENTRY_LEARNED) { /* Make sure the MA Table entry matches the entry * in the update event. */ if (switchPtr->RemoveStaleLearnEvent != NULL && switchPtr->RemoveStaleLearnEvent(sw, updateEvent, i)) { /* The learn event has been removed. * Do not update 'i', since it now contains the * following event (if any). */ fmDbgDiagCountIncr(sw, FM_CTR_MAC_LEARN_DISCARDED, 1); } else { fmDbgDiagCountIncr(sw, FM_CTR_MAC_ALPS_LEARN, 1); i++; } } else { if (fpUpdateEvent->event == FM_EVENT_ENTRY_AGED) { fmDbgDiagCountIncr(sw, FM_CTR_MAC_ALPS_AGE, 1); } i++; } } /* If all updates have been removed, don't distribute the event */ if (updateEvent->numUpdates > 0) { distributeEvent = TRUE; } break; case FM_EVENT_SECURITY: case FM_EVENT_FRAME: case FM_EVENT_SOFTWARE: case FM_EVENT_PARITY_ERROR: case FM_EVENT_FIBM_THRESHOLD: case FM_EVENT_CRM: case FM_EVENT_ARP: case FM_EVENT_EGRESS_TIMESTAMP: case FM_EVENT_PLATFORM: case FM_EVENT_LOGICAL_PORT: distributeEvent = TRUE; break; default: FM_LOG_WARNING(FM_LOG_CAT_EVENT_PORT, "Received unknown event %d\n", event->type); break; } /* end switch (event->type) */ if (distributeEvent) { if (switchIsProtected) { UNPROTECT_SWITCH(sw); switchIsProtected = FALSE; } if (eventHandler != NULL) { if (enableFramePriority && ( (event->type == FM_EVENT_PKT_RECV) || (event->type == FM_EVENT_SFLOW_PKT_RECV) ) ) { rcvPktEvent = &event->info.fpPktEvent; err = fmFreeBufferQueueNode(sw, rcvPktEvent); if (err != FM_OK) { FM_LOG_ERROR(FM_LOG_CAT_EVENT_PKT_RX, "Freeing Buffer queue node from the queue failed" "status = %d (%s) \n", err, fmErrorMsg(err)); } } eventHandler(event); } else { fmDistributeEvent(event); } } FINISHED: fmReleaseEvent(event); /* release the switch lock if any is held */ if (switchIsProtected) { UNPROTECT_SWITCH(sw); } } /* end while (1) */ fmExitThread(thread); return NULL; } /* end fmGlobalEventHandler */
/** UsedSweeperActiveState * \ingroup intFastMaint * * \desc The MAC Address USED Table sweep is in progress. * * \param[in] sw is the switch on which to operate * * \param[in] currentTime is the current value of the aging timer. * * \return None. * *****************************************************************************/ static void UsedSweeperActiveState(fm_int sw, fm_uint64 currentTime) { fm_switch * switchPtr; fm10000_switch *switchExt; fm_sweepStats stats; fm_int upperBound; fm_int numWords; switchPtr = GET_SWITCH_PTR(sw); switchExt = switchPtr->extension; FM_CLEAR(stats); upperBound = switchExt->usedTableSweeperIndex + USED_TABLE_SAMPLE_SIZE; if (upperBound > USED_TABLE_SIZE) { upperBound = USED_TABLE_SIZE; } numWords = USED_TABLE_SAMPLE_UNIT; while (switchExt->usedTableSweeperIndex < upperBound) { if ((switchExt->usedTableSweeperIndex + numWords) > upperBound) { numWords = upperBound - switchExt->usedTableSweeperIndex; } ProcessSample(sw, switchExt->usedTableSweeperIndex, numWords, currentTime, switchExt->usedTableAgingTime, switchExt->usedTableExpiryTime, &stats); switchExt->usedTableSweeperIndex += numWords; } /* end while (switchExt->usedTableSweeperIndex < upperBound) */ switchExt->usedTableNumExpired += stats.expired; if (switchExt->usedTableSweeperIndex >= USED_TABLE_SIZE) { if (stats.young || stats.old || stats.expired) { FM_LOG_DEBUG(FM_LOG_CAT_EVENT_FAST_MAINT, "sw=%d young=%d old=%d expired=%d elapsed=%llu\n", sw, stats.young, stats.old, stats.expired, currentTime - switchExt->usedTableLastSweepTime); } if (switchExt->usedTableNumExpired) { fm_maWorkTypeData data; fm_status err; FM_CLEAR(data); err = fmEnqueueMAPurge(sw, FM_UPD_FLUSH_EXPIRED, data, NULL, NULL); if (err != FM_OK) { FM_LOG_ERROR(FM_LOG_CAT_EVENT_FAST_MAINT, "fmEnqueueMAPurge failed: %s\n", fmErrorMsg(err)); } } /* Enter READY state to wait for next pass. */ switchExt->usedTableSweeperState = FM_USED_SWEEPER_READY; FM_LOG_DEBUG(FM_LOG_CAT_EVENT_FAST_MAINT, "sw=%d state=READY\n", sw); } } /* end UsedSweeperActiveState */
/** fmRawPacketSocketHandlingInitialize * \ingroup intPlatformCommon * * \desc Initializes the raw packet socket transfer module. * * \param[in] sw is the switch number to initialize. * * \param[in] hasFcs is TRUE if the packet includes the FCS field. * * \param[in] iface is a string containing the netdev's interface name * through which the packets should be sent / received. * * \return FM_OK if successful. * *****************************************************************************/ fm_status fmRawPacketSocketHandlingInitialize(fm_int sw, fm_bool hasFcs, fm_text iface) { fm_status err = FM_OK; fm_int rawSock = -1; struct ifreq ifr; struct sockaddr_ll sa; struct ethtool_value ethValue; #ifdef ENABLE_TIMESTAMP struct ifreq hwtstamp; struct hwtstamp_config hwconfig; struct hwtstamp_config hwconfig_requested; fm_int val; socklen_t len; fm_int so_timestamping_flags; struct cmsghdr * cmsg; #endif char strErrBuf[FM_STRERROR_BUF_SIZE]; errno_t strErrNum; fm_switch *switchPtr; FM_LOG_ENTRY(FM_LOG_CAT_PLATFORM, "sw=%d hasFcs=%s\n", sw, FM_BOOLSTRING(hasFcs)); if (iface == NULL) { err = FM_ERR_INVALID_ARGUMENT; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_PLATFORM, err); } err = fmGenericPacketHandlingInitializeV2(sw, hasFcs); FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_PLATFORM, err); /* initialize the raw packet socket */ rawSock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_XDSA)); if (rawSock == -1) { FM_LOG_FATAL(FM_LOG_CAT_PLATFORM, "couldn't create raw packet socket\n"); err = FM_FAIL; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_PLATFORM, err); } /* retrieve ethernet interface index */ FM_STRNCPY_S(ifr.ifr_name, IF_NAMESIZE, iface, IF_NAMESIZE); if (ioctl(rawSock, SIOCGIFINDEX, &ifr) == -1) { FM_LOG_FATAL(FM_LOG_CAT_PLATFORM, "Failed to retrieve index for interface %s\n", iface); err = FM_ERR_INVALID_ARGUMENT; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_PLATFORM, err); } /* initialize sockaddr_ll */ FM_CLEAR(sa); sa.sll_family = PF_PACKET; sa.sll_protocol = htons(ETH_P_XDSA); sa.sll_ifindex = ifr.ifr_ifindex; if (bind(rawSock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { FM_LOG_FATAL(FM_LOG_CAT_PLATFORM, "Failed to bind to the raw packet socket\n"); err = FM_FAIL; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_PLATFORM, err); } #ifdef ENABLE_TIMESTAMP /*Set timestamp related configurations */ FM_CLEAR(hwconfig); FM_CLEAR(hwconfig_requested); FM_CLEAR(hwtstamp); strncpy(hwtstamp.ifr_name, iface, IF_NAMESIZE); hwtstamp.ifr_data = (void *)&hwconfig; hwconfig.rx_filter = HWTSTAMP_FILTER_ALL; hwconfig_requested = hwconfig; errno = 0; if (ioctl(rawSock, SIOCSHWTSTAMP, &hwtstamp) < 0) { strErrNum = FM_STRERROR_S(strErrBuf, FM_STRERROR_BUF_SIZE, errno); if (strErrNum == 0) { FM_LOG_ERROR(FM_LOG_CAT_PLATFORM, "Failed to configure RX hardware timestamp for all" " received packets. Error: %s\n", strErrBuf); } else { FM_LOG_ERROR(FM_LOG_CAT_PLATFORM, "Failed to configure RX hardware timestamp for all" " received packets. Error: %d\n", errno); } } FM_LOG_DEBUG(FM_LOG_CAT_PLATFORM, "SIOCSHWTSTAMP: tx_type %d requested, got %d; " " rx_filter %d requested, got %d\n", hwconfig_requested.tx_type, hwconfig.tx_type, hwconfig_requested.rx_filter, hwconfig.rx_filter); so_timestamping_flags = SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE; if (setsockopt(rawSock, SOL_SOCKET, SO_TIMESTAMPING, &so_timestamping_flags, sizeof(so_timestamping_flags)) < 0) { FM_LOG_ERROR(FM_LOG_CAT_PLATFORM, "Failed to set timestamping on socket\n"); /* Continue without timestamp */ } len = sizeof(val); errno = 0; if (getsockopt(rawSock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) { strErrNum = FM_STRERROR_S(strErrBuf, FM_STRERROR_BUF_SIZE, errno); if (strErrNum == 0) { FM_LOG_ERROR(FM_LOG_CAT_PLATFORM, "Failed to retrieve SO_TIMESTAMPING socket options." " Error %s", strErrBuf); } else { FM_LOG_ERROR(FM_LOG_CAT_PLATFORM, "Failed to retrieve SO_TIMESTAMPING socket options." " Error %d", errno); } } else { FM_LOG_DEBUG(FM_LOG_CAT_PLATFORM, "SO_TIMESTAMPING %d\n", val); if (val != so_timestamping_flags) { FM_LOG_ERROR(FM_LOG_CAT_PLATFORM, "Expected value %d but retrieved %d\n", so_timestamping_flags, val); } } #endif /* Set the Driver in ies-tagging mode on management PEP */ ethValue.cmd = ETHTOOL_SPFLAGS; ethValue.data = ETHTOOL_PRV_FLAG_IES; ifr.ifr_data = (void*) ðValue; if (ioctl(rawSock, SIOCETHTOOL, &ifr) == -1) { strErrNum = FM_STRERROR_S(strErrBuf, FM_STRERROR_BUF_SIZE, errno); if (strErrNum == 0) { FM_LOG_ERROR(FM_LOG_CAT_PLATFORM, "Failed to set ies-tagging: %s\n", strErrBuf); } else { FM_LOG_ERROR(FM_LOG_CAT_PLATFORM, "Failed to set ies-tagging: %d\n", errno); } } if (ioctl(rawSock, SIOCGIFFLAGS, &ifr) == -1) { strErrNum = FM_STRERROR_S(strErrBuf, FM_STRERROR_BUF_SIZE, errno); if (strErrNum == 0) { FM_LOG_FATAL(FM_LOG_CAT_PLATFORM, "Failed to get socket flags: %s\n", strErrBuf); } else { FM_LOG_FATAL(FM_LOG_CAT_PLATFORM, "Failed to get socket flags: %d\n", errno); } err = FM_FAIL; FM_LOG_ABORT(FM_LOG_CAT_PLATFORM, err); } if ((ifr.ifr_flags & IFF_UP) == 0) { /* Bring the NIC up */ ifr.ifr_flags |= IFF_UP; if (ioctl(rawSock, SIOCSIFFLAGS, &ifr) == -1) { strErrNum = FM_STRERROR_S(strErrBuf, FM_STRERROR_BUF_SIZE, errno); if (strErrNum == 0) { FM_LOG_FATAL(FM_LOG_CAT_PLATFORM, "Failed to bring up: %s\n", strErrBuf); } else { FM_LOG_FATAL(FM_LOG_CAT_PLATFORM, "Failed to bring up: %d\n", errno); } err = FM_FAIL; FM_LOG_ABORT(FM_LOG_CAT_PLATFORM, err); } } GET_PLAT_STATE(sw)->rawSocket = rawSock; FM_STRNCPY_S(GET_PLAT_STATE(sw)->ifaceName, IF_NAMESIZE, iface, IF_NAMESIZE); /* Create the receive packet thread */ err = fmCreateThread("raw_packet_socket receive", FM_EVENT_QUEUE_SIZE_NONE, &fmRawPacketSocketReceivePackets, &(GET_PLAT_STATE(sw)->sw), GET_PLAT_RAW_LISTENER(sw)); switchPtr = GET_SWITCH_PTR(sw); if (switchPtr) { switchPtr->isRawSocketInitialized = FM_ENABLED; } ABORT: if ( (err != FM_OK) && (rawSock != -1) ) { close(rawSock); } FM_LOG_EXIT(FM_LOG_CAT_PLATFORM, err); } /* end fmRawPacketSocketHandlingInitialize */
/** fmMapLogicalPortToPhysical * \ingroup intSwitch * * \desc Maps a logical port number to a physical port number. * * \param[in] sstate points to the switch state entry. * * \param[in] logPort is the logical port number. Must be a cardinal * port or one of the supported non-cardinal port types * (LAG, REMOTE, VIRTUAL). * * \param[out] physPort points to the location to receive the physical * port number. * * \return FM_OK if successful. * \return FM_ERR_INVALID_PORT if logPort is not a valid port number. * \return FM_ERR_NO_PORTS_IN_LAG if logPort refers to an empty LAG. * \return FM_ERR_INVALID_ARGUMENT if there is no port structure * allocated for given port number. * *****************************************************************************/ fm_status fmMapLogicalPortToPhysical(fm_switch *sstate, fm_int logPort, fm_int * physPort) { fm_int sw; fm_int dummySw; fm_int intPort; fm_status err; sw = sstate->switchNumber; intPort = logPort; /* * This function is used primarily by code that needs to program the * hardware. It is generally applied to cardinal ports. * * In some cases, however, the function may be applied to certain * non-cardinal port types. In these cases, the logical port must be * reduced to a cardinal port. */ if (!fmIsCardinalPort(sw, intPort)) { /* If this is a remote logical port, get the internal port. */ if (fmIsRemotePort(sw, intPort)) { err = fmGetInternalPortFromRemotePort(sw, intPort, &intPort); if (err != FM_OK) { return err; } } /* If this is a LAG logical port, get one of its member ports. */ if (fmIsLagPort(sw, intPort)) { err = MapLagPortToMemberPort(sw, intPort, &intPort); if (err != FM_OK) { return err; } } /* If this is a virtual logical port, get PEP logical port. */ if (fmIsVirtualPort(sw, intPort)) { err = MapVirtualPortToPepPort(sw, intPort, &intPort); if (err != FM_OK) { return err; } } /* Verify that the end result is a cardinal port. */ if (!fmIsCardinalPort(sw, intPort)) { #if 1 /* A failure at this point probably means that there is a bug * in the API. Generate an error that will register in the * regression test system. */ if (intPort != logPort) { FM_LOG_ERROR(FM_LOG_CAT_SWITCH, "Port %d (%s) maps to non-cardinal port %d (%s)\n", logPort, fmGetPortTypeAsText(sw, logPort), intPort, fmGetPortTypeAsText(sw, intPort)); } else { FM_LOG_ERROR(FM_LOG_CAT_SWITCH, "Port %d (%s) does not map to a cardinal port\n", logPort, fmGetPortTypeAsText(sw, intPort)); } #endif /* Implement FM_ERR_INVALID_PORT_TYPE? */ return FM_ERR_INVALID_PORT; } } /* end !fmIsCardinalPort(sw, intPort) */ return fmPlatformMapLogicalPortToPhysical(sw, intPort, &dummySw, physPort); } /* end fmMapLogicalPortToPhysical */
/** fmRawPacketSocketReceivePackets * \ingroup intPlatformCommon * * \desc Handles reception of packets by raw packet socket. * * \param[in] args is a pointer to the switch number. * * \return FM_OK if successful. * *****************************************************************************/ void * fmRawPacketSocketReceivePackets(void *args) { fm_thread * thread; fm_int sw; fm_buffer * recvChainHead = NULL; fm_buffer * nextBuffer; struct pollfd rfds; struct msghdr msg; struct iovec iov[UIO_MAXIOV]; struct ifreq ifr; fm_int retval; fm_int availableBuffers; fm_int len; fm_int iov_offset; fm_int iov_count = 0; fm_int maxMtu = 0; fm_int newMtu; fm_status status; char strErrBuf[FM_STRERROR_BUF_SIZE]; errno_t strErrNum; fm_byte rawTS[8]; fm_pktSideBandData sbData; #ifdef ENABLE_TIMESTAMP struct cmsghdr * cmsg; union { struct cmsghdr cm; char control[512]; } control; #endif thread = FM_GET_THREAD_HANDLE(args); sw = *(FM_GET_THREAD_PARAM(fm_int, args)); FM_NOT_USED(thread); /* If logging is disabled, thread won't be used */ FM_LOG_ENTRY(FM_LOG_CAT_SWITCH, "thread = %s, sw = %d\n", thread->name, sw); /* initialize the message header */ FM_CLEAR(msg); msg.msg_name = NULL; /* Optional field */ msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 0; msg.msg_flags = 0; #ifdef ENABLE_TIMESTAMP msg.msg_control = &control; msg.msg_controllen = sizeof(control); #endif /* Setup the name of the interface */ FM_STRNCPY_S(ifr.ifr_name, sizeof(ifr.ifr_name), GET_PLAT_STATE(sw)->ifaceName, sizeof(GET_PLAT_STATE(sw)->ifaceName)); /* Prepare the pollfd struct */ rfds.fd = GET_PLAT_STATE(sw)->rawSocket; rfds.events = POLLIN; rfds.revents = 0; /************************************************** * Loop forever calling packet receive handler. **************************************************/ while (TRUE) { errno = 0; retval = poll(&rfds, 1, FM_FDS_POLL_TIMEOUT_USEC); if (retval == -1) { strErrNum = FM_STRERROR_S(strErrBuf, FM_STRERROR_BUF_SIZE, errno); if (strErrNum == 0) { FM_LOG_WARNING(FM_LOG_CAT_SWITCH, "ERROR: select failed: %s!\n", strErrBuf); } else { FM_LOG_WARNING(FM_LOG_CAT_SWITCH, "ERROR: select failed: %d!\n", errno); } /* Switch was removed, kill the thread */ if (GET_SWITCH_PTR(sw) == NULL) { break; } continue; } else if (!retval) { /* Switch was removed, kill the thread */ if (GET_SWITCH_PTR(sw) == NULL) { break; } continue; /* timeout */ } /* get the number of available buffers from the buffer manager*/ fmPlatformGetAvailableBuffers(&availableBuffers); if (availableBuffers <= FM_RECV_BUFFER_THRESHOLD) { /* wait for buffer to come back, before dequeueing data */ fmYield(); continue; } if (ioctl(GET_PLAT_STATE(sw)->rawSocket, SIOCGIFMTU, &ifr) == -1) { FM_LOG_WARNING(FM_LOG_CAT_SWITCH, "WARNING: failed to read netdev MTU\n"); continue; } else { newMtu = ifr.ifr_mtu; } /* MTU Size change */ if (newMtu != maxMtu) { if (recvChainHead != NULL) { /* release the existing buffer chain */ status = fmFreeBufferChain(FM_FIRST_FOCALPOINT, recvChainHead); if (status != FM_OK) { FM_LOG_ERROR( FM_LOG_CAT_SWITCH, "Unable to release prior buffer chain, " "status = %d (%s)\n", status, fmErrorMsg(status) ); } recvChainHead = NULL; } /* compute new buffer count */ iov_count = newMtu / FM_BUFFER_SIZE_BYTES; if (newMtu % FM_BUFFER_SIZE_BYTES) { iov_count++; } maxMtu = newMtu; } if (recvChainHead == NULL) { /* allocate a new buffer chain and initialize iovec array */ msg.msg_iovlen = 0; /* 8-Byte Timestamp IOV */ iov[msg.msg_iovlen].iov_base = rawTS; iov[msg.msg_iovlen].iov_len = sizeof(rawTS); msg.msg_iovlen++; for (iov_offset = 0 ; iov_offset < iov_count ; iov_offset++) { do { nextBuffer = fmAllocateBuffer(FM_FIRST_FOCALPOINT); if (nextBuffer == NULL) { /* Wait a little while for buffer to return */ fmDbgGlobalDiagCountIncr(FM_GLOBAL_CTR_RX_OUT_OF_BUFFERS, 1); fmYield(); } } while (nextBuffer == NULL); if (recvChainHead == NULL) { recvChainHead = nextBuffer; nextBuffer->next = NULL; } else { status = fmAddBuffer(recvChainHead, nextBuffer); if (status != FM_OK) { FM_LOG_ERROR( FM_LOG_CAT_SWITCH, "Unable to add buffer %d (%p) to chain %p\n", iov_offset, (void *) nextBuffer, (void *) recvChainHead ); break; } } iov[msg.msg_iovlen].iov_base = nextBuffer->data; iov[msg.msg_iovlen].iov_len = FM_BUFFER_SIZE_BYTES; msg.msg_iovlen++; } } /* now receive from the driver */ len = recvmsg(GET_PLAT_STATE(sw)->rawSocket, &msg, 0); if (len == -1) { continue; } #ifdef ENABLE_TIMESTAMP for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if ( (cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMPING) && (cmsg->cmsg_len == CMSG_LEN(sizeof(struct timespec) * 3)) ) { struct timespec *stamp = (struct timespec *)CMSG_DATA(cmsg); /* cmsg has 3 different timestamps. Timestamp we are interested is * located in index 2 */ sbData.ingressTimestamp.seconds = ( (fm_int64)(stamp[2].tv_sec) ); sbData.ingressTimestamp.nanoseconds = ( (fm_int64)(stamp[2].tv_nsec) ); } else { FM_LOG_WARNING(FM_LOG_CAT_PLATFORM, "Unknown control message of level %d type %d len %zu received\n", cmsg->cmsg_level, cmsg->cmsg_type, cmsg->cmsg_len); } } #endif /* Remove the timestamp's length to get the length of the actual * packet */ len -= sizeof(rawTS); /* The raw socket does not carry the FCS in either tx or rx, however * the API expects it to be present. Because the API clears the * FCS value before sending the packet event to the application, don't * bother about setting the correct FCS value and just increment the * length. The FCS value is undefined (whatever is in the fm_buffer at * the FCS position). */ len += 4; /* fill in the used buffer sizes */ nextBuffer = recvChainHead; while (nextBuffer != NULL) { if (len > FM_BUFFER_SIZE_BYTES) { nextBuffer->len = FM_BUFFER_SIZE_BYTES; } else { nextBuffer->len = len; } len -= nextBuffer->len; if ( (len <= 0) && (nextBuffer->next != NULL) ) { status = fmFreeBufferChain(FM_FIRST_FOCALPOINT, nextBuffer->next); if (status != FM_OK) { FM_LOG_ERROR( FM_LOG_CAT_SWITCH, "Unable to release unused buffer chain, " "status = %d (%s)\n", status, fmErrorMsg(status) ); } nextBuffer->next = NULL; } nextBuffer = nextBuffer->next; } if (recvChainHead == NULL) { continue; } /* Store the raw timestamp in 64b format */ sbData.rawTimeStamp = ((fm_uint64) (rawTS[0] & 0xFF)) << 56; sbData.rawTimeStamp |= ((fm_uint64) (rawTS[1] & 0xFF)) << 48; sbData.rawTimeStamp |= ((fm_uint64) (rawTS[2] & 0xFF)) << 40; sbData.rawTimeStamp |= ((fm_uint64) (rawTS[3] & 0xFF)) << 32; sbData.rawTimeStamp |= ((fm_uint64) (rawTS[4] & 0xFF)) << 24; sbData.rawTimeStamp |= ((fm_uint64) (rawTS[5] & 0xFF)) << 16; sbData.rawTimeStamp |= ((fm_uint64) (rawTS[6] & 0xFF)) << 8; sbData.rawTimeStamp |= ((fm_uint64) (rawTS[7] & 0xFF)); /* Don't provide an ISL tag pointer, let the API handle the ISL * tag information (included in the fm_buffer chain). */ status = fmPlatformReceiveProcessV2(sw, recvChainHead, NULL, &sbData); if (status != FM_OK) { FM_LOG_ERROR( FM_LOG_CAT_SWITCH, "Returned error status %d " "(%s)\n", status, fmErrorMsg(status) ); } /* Buffer chain has now been consumed */ recvChainHead = NULL; } /* end while (TRUE) */ fmExitThread(thread); return NULL; } /* end fmRawPacketSocketReceivePackets */
/** PerformPurge * \ingroup intMacMaint * * \desc Initiates an MA table purge operation. * * \param[in] sw is the switch on which to operate. * * \return FM_OK if successful. * *****************************************************************************/ static fm_status PerformPurge(fm_int sw) { fm_switch * switchPtr; fm_bool l2Locked; fm_int numSkipped; fm_event * eventPtr; fm_uint32 numUpdates; fm_int entryIndex; fm_status err; fm_internalMacAddrEntry * cachePtr; fm_internalMacAddrEntry oldEntry; FM_LOG_ENTRY(FM_LOG_CAT_EVENT_MAC_MAINT, "sw=%d\n", sw); switchPtr = GET_SWITCH_PTR(sw); l2Locked = FALSE; numSkipped = 0; eventPtr = NULL; numUpdates = 0; /*************************************************** * Preallocate an event buffer. **************************************************/ /* NOTE: This call will block if the number of free event * buffers drops below the acceptable threshold. */ eventPtr = fmAllocateEvent(sw, FM_EVID_HIGH_TABLE_UPDATE, FM_EVENT_TABLE_UPDATE, FM_EVENT_PRIORITY_LOW); if (eventPtr == NULL) { fmDbgDiagCountIncr(sw, FM_CTR_MAC_EVENT_ALLOC_ERR, 1); FM_LOG_ERROR(FM_LOG_CAT_EVENT_MAC_MAINT, "out of event buffers\n"); } /*************************************************** * Iterate over MAC table cache. **************************************************/ for ( entryIndex = 0 ; entryIndex < switchPtr->macTableSize ; ++entryIndex ) { if (!l2Locked) { FM_TAKE_L2_LOCK(sw); l2Locked = TRUE; numSkipped = 0; } cachePtr = &switchPtr->maTable[entryIndex]; /*************************************************** * Determine whether to purge this entry. **************************************************/ if (!MeetsPurgeCriteria(sw, cachePtr)) { ++numSkipped; if (numSkipped >= SKIP_THRESHOLD) { FM_DROP_L2_LOCK(sw); l2Locked = FALSE; } continue; } if (FM_IS_TEST_TRACE_ADDRESS(cachePtr->macAddress)) { FM_LOG_PRINT("fm10000HandlePurgeRequest(%d): " "purging mac address " FM_FORMAT_ADDR "/%u\n", sw, cachePtr->macAddress, cachePtr->vlanID); } /*************************************************** * Remove entry from MAC table. **************************************************/ /* Make a copy of the cache entry before we invalidate it. * We'll need it to generate the AGED event. */ oldEntry = *cachePtr; /* Invalidate cache and hardware entry. */ err = fm10000InvalidateEntryAtIndex(sw, entryIndex); if (err != FM_OK) { FM_LOG_ERROR(FM_LOG_CAT_EVENT_MAC_MAINT, "Error purging MA table entry: sw=%d index=%d\n", sw, entryIndex); } /*************************************************** * Generate an AGED event. **************************************************/ /* Relinquish lock before generating event. */ FM_DROP_L2_LOCK(sw); l2Locked = FALSE; fmGenerateUpdateForEvent(sw, &fmRootApi->eventThread, FM_EVENT_ENTRY_AGED, FM_MAC_REASON_PURGE_AGED, entryIndex, &oldEntry, &numUpdates, &eventPtr); fmDbgDiagCountIncr(sw, FM_CTR_MAC_PURGE_AGED, 1); } /* end for ( entryIndex = 0 ; ... ) */ if (l2Locked) { FM_DROP_L2_LOCK(sw); } if (numUpdates != 0) { fmSendMacUpdateEvent(sw, &fmRootApi->eventThread, &numUpdates, &eventPtr, FALSE); } if (eventPtr != NULL) { fmReleaseEvent(eventPtr); } err = FinishPurge(sw); FM_LOG_EXIT(FM_LOG_CAT_EVENT_MAC_MAINT, err); } /* end PerformPurge */
/** fmLoadDynamicLoadLibrary * \ingroup intAlosDynLoadLib * * \desc Load a Dynamic Load Library for a process. * This function is called when a dynamic load library * has been loaded and is in use, but the current process * has not yet loaded it. * * \param[in] handle contains the dynamic-load-library's handle. * * \return FM_ERR_UNINITIALIZED if the ALOS subsystem has not been * properly initialized. * \return FM_ERR_INVALID_ARGUMENT if one of the arguments is invalid. * \return FM_OK if successful. * *****************************************************************************/ fm_status fmLoadDynamicLoadLibrary(fm_int handle) { fm_status err; fm_dynLoadLib *lib; fm_bool lockTaken = FALSE; void * libHandle; FM_LOG_ENTRY(FM_LOG_CAT_ALOS_DLLIB, "handle = %d\n", handle); if (fmRootAlos == NULL) { FM_LOG_EXIT(FM_LOG_CAT_ALOS_DLLIB, FM_ERR_UNINITIALIZED); } if ( (handle < 0) || (handle >= FM_ALOS_INTERNAL_DYN_LOAD_LIBS) ) { FM_LOG_EXIT(FM_LOG_CAT_ALOS_DLLIB, FM_ERR_INVALID_ARGUMENT); } err = fmCaptureLock(&fmRootAlos->dlAccessLock, FM_WAIT_FOREVER); FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_ALOS_DLLIB, err); lockTaken = TRUE; lib = fmRootAlos->dlLibs[handle]; if (lib == NULL) { err = FM_ERR_INVALID_ARGUMENT; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_ALOS_DLLIB, err); } if ( ( fmProcessDynLoadLibStatus & (1 << handle) ) == 0 ) { libHandle = dlopen(lib->filePath, RTLD_NOW | RTLD_GLOBAL); if (libHandle == NULL) { char *errMsg = dlerror(); FM_LOG_ERROR(FM_LOG_CAT_ALOS_DLLIB, "Error opening library %s: %s\n", lib->filePath, errMsg); err = FM_ERR_NOT_FOUND; FM_LOG_ABORT_ON_ERR(FM_LOG_CAT_ALOS_DLLIB, err); } ProcessHandles[handle] = libHandle; lib->useCount++; fmProcessDynLoadLibStatus |= FM_LITERAL_U64(1) << handle; } err = FM_OK; ABORT: if (lockTaken) { fmReleaseLock(&fmRootAlos->dlAccessLock); } FM_LOG_EXIT(FM_LOG_CAT_ALOS_DLLIB, err); } /* end fmLoadDynamicLoadLibrary */
/* I2cWriteRead * \ingroup intSwitch * * \desc Write to then immediately read from an I2C device with * handling max response bytes using switch as I2C master. * * \param[in] sw is the switch number. * * \param[in] device is the I2C device address (0x00 - 0x7F). * * \param[in,out] data points to an array from which data is written and * into which data is read. * * \param[in] wl is the number of bytes to write. * * \param[in] rl is the number of bytes to read. * * \return FM_OK if successful. * \return Other ''Status Codes'' as appropriate in case of * failure. * *****************************************************************************/ static fm_status I2cWriteRead(fm_int sw, fm_uint device, fm_byte *data, fm_uint wl, fm_uint rl) { fm_status status; fm_switch *switchPtr; fm_uint32 regValue; fm_uint i; fm_uint j; fm_bool isTimeout; fm_timestamp start; fm_timestamp end; fm_timestamp diff; fm_uint delTime; fm_uint32 tmp; FM_LOG_ENTRY_VERBOSE(FM_LOG_CAT_SWITCH, "sw=%d device=0x%x wl=%d rl=%d\n", sw, device, wl, rl); if (rl > I2C_MAX_LEN) { FM_LOG_EXIT_VERBOSE(FM_LOG_CAT_SWITCH, FM_ERR_INVALID_ARGUMENT); } if (wl > I2C_MAX_LEN) { FM_LOG_EXIT_VERBOSE(FM_LOG_CAT_SWITCH, FM_ERR_INVALID_ARGUMENT); } if (data == NULL) { FM_LOG_EXIT_VERBOSE(FM_LOG_CAT_SWITCH, FM_ERR_INVALID_ARGUMENT); } switchPtr = GET_SWITCH_PTR(sw); for (i = 0 ; i < (wl+3)/4 ; i++) { regValue = 0; for (j = 0 ; j < 4 ; j++) { if ((i*4 + j) < wl) { regValue |= (data[i*4 + j] << ((3 - j)*8)); } } status = switchPtr->WriteUINT32(sw, FM10000_I2C_DATA(i), regValue); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_SWITCH, status); FM_LOG_DEBUG_VERBOSE(FM_LOG_CAT_SWITCH, "WRITEDATA#%d : 0x%08x\n",i, regValue); } regValue = 0; FM_SET_FIELD(regValue, FM10000_I2C_CTRL, Addr, (device << 1)); FM_SET_FIELD(regValue, FM10000_I2C_CTRL, Command, 0); FM_SET_FIELD(regValue, FM10000_I2C_CTRL, LengthW, wl); FM_SET_FIELD(regValue, FM10000_I2C_CTRL, LengthR, rl); FM_LOG_DEBUG_VERBOSE(FM_LOG_CAT_SWITCH, "WRITE: eg 0x%08x Cmd %d wl %d rl %d " "Comp %x LenSent %d intr %d\n", regValue, FM_GET_FIELD(regValue, FM10000_I2C_CTRL, Command), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthW), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthR), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthSent), FM_GET_BIT(regValue, FM10000_I2C_CTRL, InterruptPending)); status = switchPtr->WriteUINT32(sw, FM10000_I2C_CTRL(), regValue); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_SWITCH, status); status = switchPtr->ReadUINT32(sw, FM10000_I2C_CTRL(), &tmp); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_SWITCH, status); FM_LOG_DEBUG_VERBOSE(FM_LOG_CAT_SWITCH, "READ: %d reg 0x%08x Cmd %d wl %d rl %d " "Comp %x LenSent %d intr %d\n", status, tmp, FM_GET_FIELD(tmp, FM10000_I2C_CTRL, Command), FM_GET_FIELD(tmp, FM10000_I2C_CTRL, LengthW), FM_GET_FIELD(tmp, FM10000_I2C_CTRL, LengthR), FM_GET_FIELD(tmp, FM10000_I2C_CTRL, CommandCompleted), FM_GET_FIELD(tmp, FM10000_I2C_CTRL, LengthSent), FM_GET_BIT(tmp, FM10000_I2C_CTRL, InterruptPending)); /* Now change command to start the transaction */ if (rl == 0) { /* Write only allow write of 0 length */ FM_SET_FIELD(regValue, FM10000_I2C_CTRL, Command, 1); } else if ((wl > 0) && (rl > 0)) { /* Write Read */ FM_SET_FIELD(regValue, FM10000_I2C_CTRL, Command, 2); } else { /* Read */ FM_SET_FIELD(regValue, FM10000_I2C_CTRL, Command, 3); } FM_LOG_DEBUG_VERBOSE(FM_LOG_CAT_SWITCH, "WRITE2: reg 0x%08x Cmd %d wl %d rl %d " "Comp %x LenSent %d intr %d\n", regValue, FM_GET_FIELD(regValue, FM10000_I2C_CTRL, Command), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthW), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthR), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthSent), FM_GET_BIT(regValue, FM10000_I2C_CTRL, InterruptPending)); status = switchPtr->WriteUINT32(sw, FM10000_I2C_CTRL(), regValue); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_SWITCH, status); status = switchPtr->ReadUINT32(sw, FM10000_I2C_CTRL(), ®Value); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_SWITCH, status); FM_LOG_DEBUG_VERBOSE(FM_LOG_CAT_SWITCH, "READ2: %d reg 0x%08x Cmd %d wl %d rl %d " "Comp %x LenSent %d intr %d\n", status, regValue, FM_GET_FIELD(regValue, FM10000_I2C_CTRL, Command), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthW), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthR), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthSent), FM_GET_BIT(regValue, FM10000_I2C_CTRL, InterruptPending)); /* Poll to check for command done */ fmGetTime(&start); isTimeout = FALSE; do { fmDelay(0, 1000*500); if (isTimeout) { FM_LOG_ERROR( FM_LOG_CAT_SWITCH, "Dev=0x%02x: Timeout (%d msec) waiting " "for I2C_CTRL(0x%x).CommandCompleted!=0. 0x%02x\n", device, I2C_TIMEOUT_MSEC, FM10000_I2C_CTRL(), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted)); FM_LOG_EXIT_VERBOSE(FM_LOG_CAT_SWITCH, FM_ERR_I2C_NO_RESPONSE); } fmGetTime(&end); fmSubTimestamps(&end, &start, &diff); delTime = diff.sec*1000 + diff.usec/1000; /* Variable isTimeout is used to improve the timeout detecting */ if (delTime > I2C_TIMEOUT_MSEC) { isTimeout = TRUE; } status = switchPtr->ReadUINT32(sw, FM10000_I2C_CTRL(), ®Value); FM_LOG_DEBUG_VERBOSE(FM_LOG_CAT_SWITCH, "STATUS: %d reg 0x%08x cmd %d wl %d rl %d " "Comp %x LenSent %d intr %d\n", status, regValue, FM_GET_FIELD(regValue, FM10000_I2C_CTRL, Command), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthW), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthR), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted), FM_GET_FIELD(regValue, FM10000_I2C_CTRL, LengthSent), FM_GET_BIT(regValue, FM10000_I2C_CTRL, InterruptPending)); if (status) break; } while (FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted) == 0); if (FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted) != 1) { FM_LOG_DEBUG_VERBOSE(FM_LOG_CAT_SWITCH, "Dev=0x%02x: I2C Command completed with error 0x%x. " "I2C_CTRL(0x%x)=0x%x\n", device, FM_GET_FIELD(regValue, FM10000_I2C_CTRL, CommandCompleted), FM10000_I2C_CTRL(), regValue); FM_LOG_EXIT_VERBOSE(FM_LOG_CAT_SWITCH, FM_ERR_I2C_NO_RESPONSE); } for (i = 0 ; i < (rl+3)/4 ; i++) { status = switchPtr->ReadUINT32(sw, FM10000_I2C_DATA(i), ®Value); FM_LOG_EXIT_ON_ERR(FM_LOG_CAT_SWITCH, status); FM_LOG_DEBUG_VERBOSE(FM_LOG_CAT_SWITCH,"READDATA#%d : 0x%08x\n",i, regValue); for (j = 0 ; j < 4 ; j++) { if ((i*4 + j) < rl) { data[i*4 + j] = (regValue >> ((3 - j)*8)) & 0xFF; } } }