vslib/SwitchState.cpp (298 lines of code) (raw):

#include "NetMsgRegistrar.h" #include "SwitchState.h" #include "RealObjectIdManager.h" #include "SwitchStateBase.h" #include "RealObjectIdManager.h" #include "EventPayloadNetLinkMsg.h" #include "swss/logger.h" #include "meta/sai_serialize.h" #include <netlink/route/link.h> #include <netlink/route/addr.h> #include <linux/if.h> using namespace saivs; #define VS_COUNTERS_COUNT_MSB (0x80000000) const std::map<sai_stat_id_t, std::string> SwitchState::m_statIdMap = { { SAI_PORT_STAT_IF_IN_OCTETS, "rx_bytes" }, { SAI_PORT_STAT_IF_IN_UCAST_PKTS, "rx_packets" }, { SAI_PORT_STAT_IF_IN_ERRORS, "rx_errors" }, { SAI_PORT_STAT_IF_IN_DISCARDS, "rx_dropped" }, { SAI_PORT_STAT_IF_OUT_OCTETS, "tx_bytes" }, { SAI_PORT_STAT_IF_OUT_UCAST_PKTS, "tx_packets" }, { SAI_PORT_STAT_IF_OUT_ERRORS, "tx_errors" }, { SAI_PORT_STAT_IF_OUT_DISCARDS, "tx_dropped" } }; SwitchState::SwitchState( _In_ sai_object_id_t switch_id, _In_ std::shared_ptr<SwitchConfig> config): m_switch_id(switch_id), m_linkCallbackIndex(-1), m_switchConfig(config) { SWSS_LOG_ENTER(); if (RealObjectIdManager::objectTypeQuery(switch_id) != SAI_OBJECT_TYPE_SWITCH) { SWSS_LOG_THROW("object %s is not SWITCH, its %s", sai_serialize_object_id(switch_id).c_str(), sai_serialize_object_type(RealObjectIdManager::objectTypeQuery(switch_id)).c_str()); } for (size_t i = 0; i < sai_metadata_enum_sai_object_type_t.valuescount; ++i) { /* * Populate empty maps for each object to avoid checking if * objecttype exists. */ m_objectHash[(sai_object_type_t)sai_metadata_enum_sai_object_type_t.values[i]] = { }; } /* * Create switch by default, it will require special treat on * creating. */ m_objectHash[SAI_OBJECT_TYPE_SWITCH][sai_serialize_object_id(switch_id)] = {}; if (m_switchConfig->m_useTapDevice) { m_linkCallbackIndex = NetMsgRegistrar::getInstance().registerCallback( std::bind(&SwitchState::asyncOnLinkMsg, this, std::placeholders::_1, std::placeholders::_2)); } if (m_switchConfig->m_resourceLimiter) { SWSS_LOG_NOTICE("resource limiter is SET on switch %s", sai_serialize_object_id(switch_id).c_str()); } } SwitchState::~SwitchState() { SWSS_LOG_ENTER(); SWSS_LOG_NOTICE("begin"); if (m_switchConfig->m_useTapDevice) { NetMsgRegistrar::getInstance().unregisterCallback(m_linkCallbackIndex); // if unregister ended then no new messages will arrive for this class // so there is no need to protect this using mutex } SWSS_LOG_NOTICE("switch %s", sai_serialize_object_id(m_switch_id).c_str()); SWSS_LOG_NOTICE("end"); } void SwitchState::setMeta( std::weak_ptr<saimeta::Meta> meta) { SWSS_LOG_ENTER(); m_meta = meta; } sai_object_id_t SwitchState::getSwitchId() const { SWSS_LOG_ENTER(); return m_switch_id; } void SwitchState::setIfNameToPortId( _In_ const std::string& ifname, _In_ sai_object_id_t port_id) { SWSS_LOG_ENTER(); m_ifname_to_port_id_map[ifname] = port_id; } void SwitchState::removeIfNameToPortId( _In_ const std::string& ifname) { SWSS_LOG_ENTER(); m_ifname_to_port_id_map.erase(ifname); } sai_object_id_t SwitchState::getPortIdFromIfName( _In_ const std::string& ifname) const { SWSS_LOG_ENTER(); auto it = m_ifname_to_port_id_map.find(ifname); if (it == m_ifname_to_port_id_map.end()) { return SAI_NULL_OBJECT_ID; } return it->second; } void SwitchState::setPortIdToTapName( _In_ sai_object_id_t port_id, _In_ const std::string& tapname) { SWSS_LOG_ENTER(); m_port_id_to_tapname[port_id] = tapname; } void SwitchState::removePortIdToTapName( _In_ sai_object_id_t port_id) { SWSS_LOG_ENTER(); m_port_id_to_tapname.erase(port_id); } bool SwitchState::getTapNameFromPortId( _In_ const sai_object_id_t port_id, _Out_ std::string& if_name) { SWSS_LOG_ENTER(); if (m_port_id_to_tapname.find(port_id) != m_port_id_to_tapname.end()) { if_name = m_port_id_to_tapname[port_id]; return true; } return false; } void SwitchState::asyncOnLinkMsg( _In_ int nlmsg_type, _In_ struct nl_object *obj) { SWSS_LOG_ENTER(); switch (nlmsg_type) { case RTM_NEWLINK: case RTM_DELLINK: break; default: SWSS_LOG_WARN("unsupported nlmsg_type: %d", nlmsg_type); return; } struct rtnl_link *link = (struct rtnl_link *)obj; int if_index = rtnl_link_get_ifindex(link); unsigned int if_flags = rtnl_link_get_flags(link); // IFF_LOWER_UP and IFF_RUNNING const char* if_name = rtnl_link_get_name(link); SWSS_LOG_NOTICE("received %s ifname: %s, ifflags: 0x%x, ifindex: %d", (nlmsg_type == RTM_NEWLINK ? "RTM_NEWLINK" : "RTM_DELLINK"), if_name, if_flags, if_index); auto payload = std::make_shared<EventPayloadNetLinkMsg>(m_switch_id, nlmsg_type, if_index, if_flags, if_name); m_switchConfig->m_eventQueue->enqueue(std::make_shared<Event>(EVENT_TYPE_NET_LINK_MSG, payload)); } sai_status_t SwitchState::getNetStat( _In_ sai_stat_id_t counterId, _In_ std::string& ifName, _Out_ uint64_t& counter) { SWSS_LOG_ENTER(); auto mapit = SwitchState::m_statIdMap.find(counterId); if (mapit != SwitchState::m_statIdMap.end()) { std::string filename = "/sys/class/net/" + ifName + "/statistics/" + mapit->second; std::ifstream istrm(filename.c_str(), std::ifstream::in); if (istrm.good()) { istrm >> counter; } else { SWSS_LOG_ERROR("failed to open ifstream in file %s", filename.c_str()); counter = -1; return SAI_STATUS_FAILURE; } } return SAI_STATUS_SUCCESS; } sai_status_t SwitchState::getPortStat( _In_ sai_object_id_t portId, _In_ const sai_stat_id_t counterId, _Out_ uint64_t& counter) { SWSS_LOG_ENTER(); std::string ifName; /* zero out counter */ counter = 0; if (getTapNameFromPortId(portId, ifName) == false) { /* * Hostif not available is expected during init. * Hostif missing after init is failure. * In both case return counter zero with debug log. */ SWSS_LOG_DEBUG("Hostif is not ready %s", sai_serialize_object_id(portId).c_str()); return SAI_STATUS_SUCCESS; } if (getNetStat(counterId, ifName, counter) != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Port stat get failed %s", ifName.c_str()); return SAI_STATUS_FAILURE; } return SAI_STATUS_SUCCESS; } sai_status_t SwitchState::getStatsExt( _In_ sai_object_type_t object_type, _In_ sai_object_id_t object_id, _In_ uint32_t number_of_counters, _In_ const sai_stat_id_t* counter_ids, _In_ sai_stats_mode_t mode, _Out_ uint64_t *counters) { SWSS_LOG_ENTER(); bool perform_set = false; auto info = sai_metadata_get_object_type_info(object_type); bool enabled = false; auto meta = m_meta.lock(); if (meta) { enabled = meta->meta_unittests_enabled(); } else { SWSS_LOG_WARN("meta pointer expired"); } if (enabled && (number_of_counters & VS_COUNTERS_COUNT_MSB )) { number_of_counters &= ~VS_COUNTERS_COUNT_MSB; SWSS_LOG_NOTICE("unittests are enabled and counters count MSB is set to 1, performing SET on %s counters (%s)", sai_serialize_object_id(object_id).c_str(), info->statenum->name); perform_set = true; } auto str_object_id = sai_serialize_object_id(object_id); auto mapit = m_countersMap.find(str_object_id); if (mapit == m_countersMap.end()) m_countersMap[str_object_id] = { }; auto& localcounters = m_countersMap[str_object_id]; for (uint32_t i = 0; i < number_of_counters; ++i) { int32_t id = counter_ids[i]; uint64_t counter; if (perform_set) { localcounters[ id ] = counters[i]; } else { auto it = localcounters.find(id); if (it == localcounters.end()) { // if counter is not found on list, just return 0 counters[i] = 0; } else { counters[i] = it->second; } /* In non unit test mode, fetch port counters from host interface */ if (!enabled && (object_type == SAI_OBJECT_TYPE_PORT)) { if (getPortStat(object_id, id, counter) != SAI_STATUS_SUCCESS) { return SAI_STATUS_FAILURE; } localcounters[ id ] = counter; } if (mode == SAI_STATS_MODE_READ_AND_CLEAR) { localcounters[ id ] = 0; } } } return SAI_STATUS_SUCCESS; } sai_status_t SwitchState::queryStatsCapability( _In_ sai_object_id_t switchId, _In_ sai_object_type_t objectType, _Inout_ sai_stat_capability_list_t *stats_capability) { SWSS_LOG_ENTER(); auto info = sai_metadata_get_object_type_info(objectType); if (stats_capability->count == 0 || stats_capability->list == nullptr) { stats_capability->count = (uint32_t)info->statenum->valuescount; return SAI_STATUS_BUFFER_OVERFLOW; } SWSS_LOG_NOTICE("query counter capability for object ID %s of counter type %s", sai_serialize_object_id(switchId).c_str(), info->statenum->name); auto statenumlist = info->statenum->values; for (uint32_t i = 0; i < stats_capability->count; i++) { stats_capability->list[i].stat_enum = statenumlist[i]; stats_capability->list[i].stat_modes = SAI_STATS_MODE_READ_AND_CLEAR; } return SAI_STATUS_SUCCESS; } std::shared_ptr<saimeta::Meta> SwitchState::getMeta() { SWSS_LOG_ENTER(); auto meta = m_meta.lock(); if (!meta) { SWSS_LOG_WARN("meta pointer expired"); } return meta; }