From 3026945b67f81e17b0e145392af7decb11e64f85 Mon Sep 17 00:00:00 2001 From: Kamil Cudnik Date: Mon, 7 Sep 2020 20:41:57 +0200 Subject: [PATCH] Add Break Before Make mechanism to syncd comparison logic (#642) * [player] Add more log info when fail response * [syncd] Send notify view response even when exception happen * [vs] Add resource limiter * [vs] Start using resource limiter * [syncd] Fix vendor sai functions names check * [syncd] Add apply view remove object sanity checks * [syncd] Lower NPU switch log to notice * [syncd] Add simlar best match candidate with most same attributes * [syncd] Add BreakConfig class * [syncd] Add break config option to command line * [syncd] Add comparison logic break before make path * [syncd] Add break config parser * [syncd] Add break config size method * [syncd] Fix command line parser help message * [syncd] Start using break config parser * Update aspell * [tests] Add unittests --- saiplayer/SaiPlayer.cpp | 6 +- syncd/AsicView.cpp | 15 ++ syncd/BestCandidateFinder.cpp | 111 ++++++++++++ syncd/BestCandidateFinder.h | 3 + syncd/BreakConfig.cpp | 49 ++++++ syncd/BreakConfig.h | 39 +++++ syncd/BreakConfigParser.cpp | 59 +++++++ syncd/BreakConfigParser.h | 23 +++ syncd/CommandLineOptions.cpp | 3 + syncd/CommandLineOptions.h | 2 + syncd/CommandLineOptionsParser.cpp | 15 +- syncd/ComparisonLogic.cpp | 213 +++++++++++++++++++++++- syncd/ComparisonLogic.h | 17 +- syncd/Makefile.am | 2 + syncd/Syncd.cpp | 25 ++- syncd/Syncd.h | 3 + syncd/VendorSai.cpp | 12 +- tests/BCM56850.pl | 8 + tests/BCM56850/bbm.ini | 1 + tests/BCM56850/limits.ini | 1 + tests/BCM56850/vsprofile.ini | 5 +- tests/BCM56850/vsprofile_acl_limit.ini | 7 + tests/aspell.en.pws | 4 + tests/utils.pm | 4 +- vslib/inc/ResourceLimiter.h | 44 +++++ vslib/inc/ResourceLimiterContainer.h | 35 ++++ vslib/inc/ResourceLimiterParser.h | 41 +++++ vslib/inc/Sai.h | 3 + vslib/inc/SwitchConfig.h | 3 + vslib/inc/saivs.h | 10 ++ vslib/src/Makefile.am | 3 + vslib/src/ResourceLimiter.cpp | 60 +++++++ vslib/src/ResourceLimiterContainer.cpp | 50 ++++++ vslib/src/ResourceLimiterParser.cpp | 163 ++++++++++++++++++ vslib/src/Sai.cpp | 10 ++ vslib/src/SwitchState.cpp | 6 + vslib/src/SwitchStateBase.cpp | 14 ++ vslib/src/VirtualSwitchSaiInterface.cpp | 2 +- 38 files changed, 1046 insertions(+), 25 deletions(-) create mode 100644 syncd/BreakConfig.cpp create mode 100644 syncd/BreakConfig.h create mode 100644 syncd/BreakConfigParser.cpp create mode 100644 syncd/BreakConfigParser.h create mode 100644 tests/BCM56850/bbm.ini create mode 100644 tests/BCM56850/limits.ini create mode 100644 tests/BCM56850/vsprofile_acl_limit.ini create mode 100644 vslib/inc/ResourceLimiter.h create mode 100644 vslib/inc/ResourceLimiterContainer.h create mode 100644 vslib/inc/ResourceLimiterParser.h create mode 100644 vslib/src/ResourceLimiter.cpp create mode 100644 vslib/src/ResourceLimiterContainer.cpp create mode 100644 vslib/src/ResourceLimiterParser.cpp diff --git a/saiplayer/SaiPlayer.cpp b/saiplayer/SaiPlayer.cpp index 14985dc86..c6bd2c09f 100644 --- a/saiplayer/SaiPlayer.cpp +++ b/saiplayer/SaiPlayer.cpp @@ -321,7 +321,8 @@ void SaiPlayer::match_list_lengths( if (get_attr_count != attr_count) { - SWSS_LOG_THROW("list number don't match %u != %u", get_attr_count, attr_count); + SWSS_LOG_THROW("list number don't match %u != %u (%s)", get_attr_count, attr_count, + sai_serialize_object_type(object_type).c_str()); } for (uint32_t i = 0; i < attr_count; ++i) @@ -447,7 +448,8 @@ void SaiPlayer::match_redis_with_rec( if (get_attr_count != attr_count) { - SWSS_LOG_THROW("list number don't match %u != %u", get_attr_count, attr_count); + SWSS_LOG_THROW("list number don't match %u != %u (%s)", get_attr_count, attr_count, + sai_serialize_object_type(object_type).c_str()); } for (uint32_t i = 0; i < attr_count; ++i) diff --git a/syncd/AsicView.cpp b/syncd/AsicView.cpp index e1b2d32a8..09cf06429 100644 --- a/syncd/AsicView.cpp +++ b/syncd/AsicView.cpp @@ -754,6 +754,11 @@ void AsicView::asicRemoveObject( SWSS_LOG_INFO("%s: %s", currentObj->m_str_object_type.c_str(), currentObj->m_str_object_id.c_str()); + if (currentObj->getObjectStatus() != SAI_OBJECT_STATUS_NOT_PROCESSED) + { + SWSS_LOG_THROW("FATAL: removing object with status: %d, logic error", currentObj->getObjectStatus()); + } + m_asicOperationId++; if (currentObj->isOidObject()) @@ -763,6 +768,16 @@ void AsicView::asicRemoveObject( * that check here also as sanity check. */ + int count = getVidReferenceCount(currentObj->getVid()); + + if (count != 0) + { + SWSS_LOG_THROW("can't remove existing object %s:%s since reference count is %d, FIXME", + currentObj->m_str_object_type.c_str(), + currentObj->m_str_object_id.c_str(), + count); + } + m_soOids.erase(currentObj->m_str_object_id); m_oOids.erase(currentObj->m_meta_key.objectkey.key.object_id); diff --git a/syncd/BestCandidateFinder.cpp b/syncd/BestCandidateFinder.cpp index dcaf336db..d02f48005 100644 --- a/syncd/BestCandidateFinder.cpp +++ b/syncd/BestCandidateFinder.cpp @@ -2358,6 +2358,117 @@ std::shared_ptr BestCandidateFinder::findCurrentBestMatch( } } +std::shared_ptr BestCandidateFinder::findSimilarBestMatch( + _In_ const std::shared_ptr &temporaryObj) +{ + SWSS_LOG_ENTER(); + + // will find object in current view that have most same attributes + + auto notProcessedObjects = m_currentView.getNotProcessedObjectsByObjectType(temporaryObj->getObjectType()); + + const auto attrs = temporaryObj->getAllAttributes(); + + SWSS_LOG_INFO("not processed objects for %s: %zu, temp attrs: %zu", + temporaryObj->m_str_object_type.c_str(), + notProcessedObjects.size(), + attrs.size()); + + std::vector candidateObjects; + + for (const auto ¤tObj: notProcessedObjects) + { + // log how many attributes current object have + + SWSS_LOG_INFO("current obj %s, attrs: %zu", + currentObj->m_str_object_id.c_str(), + currentObj->getAllAttributes().size()); + + sai_object_compare_info_t soci = { 0, currentObj }; + + /* + * NOTE: we only iterate by attributes that are present in temporary + * view. It may happen that current view has some additional attributes + * set that are create only and value can't be updated, we ignore that + * since we want to find object with most matching attributes. + */ + + for (const auto &attr: attrs) + { + sai_attr_id_t attrId = attr.first; + + // Function hasEqualAttribute check if attribute exists on both objects. + + if (hasEqualAttribute(m_currentView, m_temporaryView, currentObj, temporaryObj, attrId)) + { + soci.equal_attributes++; + + SWSS_LOG_INFO("ob equal %s %s, %s: %s", + temporaryObj->m_str_object_id.c_str(), + currentObj->m_str_object_id.c_str(), + attr.second->getStrAttrId().c_str(), + attr.second->getStrAttrValue().c_str()); + } + else + { + SWSS_LOG_INFO("ob not equal %s %s, %s: %s", + temporaryObj->m_str_object_id.c_str(), + currentObj->m_str_object_id.c_str(), + attr.second->getStrAttrId().c_str(), + attr.second->getStrAttrValue().c_str()); + } + } + + // NOTE: we could check if current object has some attributes that are + // default value on temporary object, and count them in + + candidateObjects.push_back(soci); + } + + SWSS_LOG_INFO("number candidate objects for %s is %zu", + temporaryObj->m_str_object_id.c_str(), + candidateObjects.size()); + + if (candidateObjects.size() == 0) + { + SWSS_LOG_WARN("not processed objects in current view is zero"); + + return nullptr; + } + + if (candidateObjects.size() == 1) + { + // We found only one object so return it as candidate + + return candidateObjects.begin()->obj; + } + + /* + * Sort candidate objects by equal attributes in descending order, we know + * here that we have at least 2 candidates. + */ + + std::sort(candidateObjects.begin(), candidateObjects.end(), compareByEqualAttributes); + + if (candidateObjects.at(0).equal_attributes > candidateObjects.at(1).equal_attributes) + { + /* + * We have only 1 object with the greatest number of equal attributes + * lets choose that object as our best match. + */ + + SWSS_LOG_INFO("eq attributes: %ld vs %ld", + candidateObjects.at(0).equal_attributes, + candidateObjects.at(1).equal_attributes); + + return candidateObjects.begin()->obj; + } + + SWSS_LOG_WARN("same number of attributes equal, selecting first"); + + return candidateObjects.begin()->obj; +} + int BestCandidateFinder::findAllChildsInDependencyTreeCount( _In_ const AsicView &view, _In_ const std::shared_ptr &obj) diff --git a/syncd/BestCandidateFinder.h b/syncd/BestCandidateFinder.h index f14de9f6e..3a2a85432 100644 --- a/syncd/BestCandidateFinder.h +++ b/syncd/BestCandidateFinder.h @@ -35,6 +35,9 @@ namespace syncd std::shared_ptr findCurrentBestMatch( _In_ const std::shared_ptr &temporaryObj); + std::shared_ptr findSimilarBestMatch( + _In_ const std::shared_ptr &temporaryObj); + private: std::shared_ptr findCurrentBestMatchForGenericObject( diff --git a/syncd/BreakConfig.cpp b/syncd/BreakConfig.cpp new file mode 100644 index 000000000..4bca01082 --- /dev/null +++ b/syncd/BreakConfig.cpp @@ -0,0 +1,49 @@ +#include "BreakConfig.h" + +#include "swss/logger.h" + +using namespace syncd; + +void BreakConfig::insert( + _In_ sai_object_type_t objectType) +{ + SWSS_LOG_ENTER(); + + m_set.insert(objectType); +} + +void BreakConfig::remove( + _In_ sai_object_type_t objectType) +{ + SWSS_LOG_ENTER(); + + auto it = m_set.find(objectType); + + if (it != m_set.end()) + { + m_set.erase(it); + } +} + +void BreakConfig::clear() +{ + SWSS_LOG_ENTER(); + + m_set.clear(); +} + +bool BreakConfig::shouldBreakBeforeMake( + _In_ sai_object_type_t objectType) const +{ + SWSS_LOG_ENTER(); + + return m_set.find(objectType) != m_set.end(); +} + +size_t BreakConfig::size() const +{ + SWSS_LOG_ENTER(); + + return m_set.size(); +} + diff --git a/syncd/BreakConfig.h b/syncd/BreakConfig.h new file mode 100644 index 000000000..0be11f0c9 --- /dev/null +++ b/syncd/BreakConfig.h @@ -0,0 +1,39 @@ +#pragma once + +extern "C"{ +#include "sai.h" +} + +#include + +namespace syncd +{ + class BreakConfig + { + public: + + BreakConfig() = default; + + ~BreakConfig() = default; + + public: + + void insert( + _In_ sai_object_type_t objectType); + + void remove( + _In_ sai_object_type_t objectType); + + void clear(); + + bool shouldBreakBeforeMake( + _In_ sai_object_type_t objectType) const; + + size_t size() const; + + private: + + std::set m_set; + + }; +} diff --git a/syncd/BreakConfigParser.cpp b/syncd/BreakConfigParser.cpp new file mode 100644 index 000000000..dae0ed630 --- /dev/null +++ b/syncd/BreakConfigParser.cpp @@ -0,0 +1,59 @@ +#include "BreakConfigParser.h" + +#include "swss/logger.h" + +#include "meta/sai_serialize.h" + +using namespace syncd; + +std::shared_ptr BreakConfigParser::parseBreakConfig( + _In_ const std::string& filePath) +{ + SWSS_LOG_ENTER(); + + auto config = std::make_shared(); + + if (filePath.size() == 0) + { + return config; // return empty config + } + + std::ifstream file(filePath); + + if (!file.is_open()) + { + SWSS_LOG_ERROR("failed to open break config file: %s: errno: %s, returning empty config", filePath.c_str(), strerror(errno)); + + return config; + } + + std::string line; + + while (getline(file, line)) + { + if (line.size() > 0 && (line[0] == '#' || line[0] == ';')) + { + continue; + } + + sai_object_type_t objectType; + + try + { + sai_deserialize_object_type(line, objectType); + + config->insert(objectType); + + SWSS_LOG_INFO("inserting %s to break config", line.c_str()); + } + catch (const std::exception& e) + { + SWSS_LOG_WARN("failed to parse '%s' as sai_object_type_t", line.c_str()); + } + } + + SWSS_LOG_NOTICE("break config parse success, contains %zu entries", config->size()); + + + return config; +} diff --git a/syncd/BreakConfigParser.h b/syncd/BreakConfigParser.h new file mode 100644 index 000000000..61aea7b96 --- /dev/null +++ b/syncd/BreakConfigParser.h @@ -0,0 +1,23 @@ +#pragma once + +#include "BreakConfig.h" + +#include + +namespace syncd +{ + class BreakConfigParser + { + private: + + BreakConfigParser() = delete; + + ~BreakConfigParser() = delete; + + public: + + static std::shared_ptr parseBreakConfig( + _In_ const std::string& filePath); + + }; +} diff --git a/syncd/CommandLineOptions.cpp b/syncd/CommandLineOptions.cpp index 8527920ee..8af89d925 100644 --- a/syncd/CommandLineOptions.cpp +++ b/syncd/CommandLineOptions.cpp @@ -27,6 +27,8 @@ CommandLineOptions::CommandLineOptions() m_contextConfig = ""; + m_breakConfig = ""; + #ifdef SAITHRIFT m_runRPCServer = false; @@ -53,6 +55,7 @@ std::string CommandLineOptions::getCommandLineString() const ss << " ProfileMapFile=" << m_profileMapFile; ss << " GlobalContext=" << m_globalContext; ss << " ContextConfig=" << m_contextConfig; + ss << " BreakConfig=" << m_breakConfig; #ifdef SAITHRIFT diff --git a/syncd/CommandLineOptions.h b/syncd/CommandLineOptions.h index 8e62b0bd1..473e38c47 100644 --- a/syncd/CommandLineOptions.h +++ b/syncd/CommandLineOptions.h @@ -76,6 +76,8 @@ namespace syncd std::string m_contextConfig; + std::string m_breakConfig; + #ifdef SAITHRIFT bool m_runRPCServer; std::string m_portMapFile; diff --git a/syncd/CommandLineOptionsParser.cpp b/syncd/CommandLineOptionsParser.cpp index b0655f85e..a3a645ef6 100644 --- a/syncd/CommandLineOptionsParser.cpp +++ b/syncd/CommandLineOptionsParser.cpp @@ -17,9 +17,9 @@ std::shared_ptr CommandLineOptionsParser::parseCommandLine( auto options = std::make_shared(); #ifdef SAITHRIFT - const char* const optstring = "dp:t:g:x:uSUCsrm:h"; + const char* const optstring = "dp:t:g:x:b:uSUCsrm:h"; #else - const char* const optstring = "dp:t:g:x:uSUCsh"; + const char* const optstring = "dp:t:g:x:b:uSUCsh"; #endif // SAITHRIFT while(true) @@ -36,6 +36,7 @@ std::shared_ptr CommandLineOptionsParser::parseCommandLine( { "syncMode", no_argument, 0, 's' }, { "globalContext", required_argument, 0, 'g' }, { "contextContig", required_argument, 0, 'x' }, + { "breakConfig", required_argument, 0, 'b' }, #ifdef SAITHRIFT { "rpcserver", no_argument, 0, 'r' }, { "portmap", required_argument, 0, 'm' }, @@ -101,6 +102,10 @@ std::shared_ptr CommandLineOptionsParser::parseCommandLine( options->m_contextConfig = std::string(optarg); break; + case 'b': + options->m_breakConfig = std::string(optarg); + break; + #ifdef SAITHRIFT case 'r': options->m_runRPCServer = true; @@ -133,9 +138,9 @@ void CommandLineOptionsParser::printUsage() SWSS_LOG_ENTER(); #ifdef SAITHRIFT - std::cout << "Usage: syncd [-d] [-p profile] [-t type] [-u] [-S] [-U] [-C] [-s] [-g idx] [-x contextConfig] [-r] [-m portmap] [-h]" << std::endl; + std::cout << "Usage: syncd [-d] [-p profile] [-t type] [-u] [-S] [-U] [-C] [-s] [-g idx] [-x contextConfig] [-b breakConfig] [-r] [-m portmap] [-h]" << std::endl; #else - std::cout << "Usage: syncd [-d] [-p profile] [-t type] [-u] [-S] [-U] [-C] [-s] [-g idx] [-x contextConfig] [-h]" << std::endl; + std::cout << "Usage: syncd [-d] [-p profile] [-t type] [-u] [-S] [-U] [-C] [-s] [-g idx] [-x contextConfig] [-b breakConfig] [-h]" << std::endl; #endif // SAITHRIFT std::cout << " -d --diag" << std::endl; @@ -158,6 +163,8 @@ void CommandLineOptionsParser::printUsage() std::cout << " Global context index to load from context config file" << std::endl; std::cout << " -x --contextConfig" << std::endl; std::cout << " Context configuration file" << std::endl; + std::cout << " -b --breakConfig" << std::endl; + std::cout << " Comparison logic 'break before make' configuration file" << std::endl; #ifdef SAITHRIFT diff --git a/syncd/ComparisonLogic.cpp b/syncd/ComparisonLogic.cpp index 69d5579c7..6b71c9fbb 100644 --- a/syncd/ComparisonLogic.cpp +++ b/syncd/ComparisonLogic.cpp @@ -28,13 +28,15 @@ ComparisonLogic::ComparisonLogic( _In_ std::shared_ptr handler, _In_ std::set initViewRemovedVids, _In_ std::shared_ptr current, - _In_ std::shared_ptr temp): + _In_ std::shared_ptr temp, + _In_ std::shared_ptr breakConfig): m_vendorSai(vendorSai), m_switch(sw), m_initViewRemovedVids(initViewRemovedVids), m_current(current), m_temp(temp), - m_handler(handler) + m_handler(handler), + m_breakConfig(breakConfig) { SWSS_LOG_ENTER(); @@ -501,6 +503,11 @@ void ComparisonLogic::removeExistingObjectFromCurrentView( { SWSS_LOG_ENTER(); + if (currentObj->getObjectStatus() != SAI_OBJECT_STATUS_NOT_PROCESSED) + { + SWSS_LOG_THROW("FATAL: removing object with status: %d, logic error", currentObj->getObjectStatus()); + } + /* * This decreasing VID reference will be hard when actual reference will * be one of default objects like CPU or default trap group when we "bring @@ -1781,6 +1788,14 @@ void ComparisonLogic::processObjectForViewTransition( temporaryObj->m_str_object_type.c_str(), temporaryObj->m_str_object_id.c_str()); + /* + * We can hit this scenario when, we will not have current best match, + * and we still want to create new object, but resources are limited, + * so it can happen that in this place we also would need to remove + * some objects first, before we create new one. + */ + breakBeforeMake(currentView, temporaryView, currentBestMatch, temporaryObj); + createNewObjectFromTemporaryObject(currentView, temporaryView, temporaryObj); return; } @@ -1856,10 +1871,12 @@ void ComparisonLogic::processObjectForViewTransition( else { /* - * Later on if we decide we want to remove objects before - * creating new one's we need to put here this action, or just - * remove this entire switch and call remove. + * Later on if we decide we want to remove objects before creating + * new one's we need to put here this action, or just remove this + * entire switch and call remove. */ + + breakBeforeMake(currentView, temporaryView, currentBestMatch, temporaryObj); } // No need to store VID since at this point we don't have RID yet, it will be @@ -1915,6 +1932,192 @@ void ComparisonLogic::processObjectForViewTransition( updateObjectStatus(currentView, temporaryView, currentBestMatch, temporaryObj); } +void ComparisonLogic::removeCurrentObjectDependencyTree( + _In_ AsicView ¤tView, + _In_ AsicView &temporaryView, + _In_ const std::shared_ptr& currentObj) +{ + SWSS_LOG_ENTER(); + + if (!currentObj->isOidObject()) + { + // we should be able to remove non object right away since it's leaf + + removeExistingObjectFromCurrentView(currentView, temporaryView, currentObj); + + return; + } + + // check reference count and remove other objects if necessary + + const int count = currentView.getVidReferenceCount(currentObj->getVid()); + + if (count) + { + SWSS_LOG_INFO("similar best match has reference count: %d, will need to remove other objects", count); + + auto* info = sai_metadata_get_object_type_info(currentObj->getObjectType()); + + // use reverse dependency graph to locate objects where current object can be used + // need to lookout on loops on some objects + + for (size_t i = 0; i < info->revgraphmemberscount; i++) + { + auto *revgraph = info->revgraphmembers[i]; + + if (revgraph->structmember) + { + SWSS_LOG_THROW("struct fields not supported yet, FIXME"); + } + + SWSS_LOG_INFO("used on %s:%s", + sai_serialize_object_type(revgraph->depobjecttype).c_str(), + revgraph->attrmetadata->attridname); + + // TODO it could be not processed, or matched, since on matched we + // can still break the link + + auto objs = currentView.getObjectsByObjectType(revgraph->depobjecttype); + + for (auto& obj: objs) + { + // NOTE: current object can have multiple OID attributes that + // they can have the same value, but they can have different + // attributes (like set/create_only) + + auto status = obj->getObjectStatus(); + + if (status != SAI_OBJECT_STATUS_NOT_PROCESSED && + status != SAI_OBJECT_STATUS_MATCHED) + { + continue; + } + + auto attr = obj->tryGetSaiAttr(revgraph->attrmetadata->attrid); + + if (attr == nullptr) + { + // no such attribute + continue; + } + + if (revgraph->attrmetadata->attrvaluetype != SAI_ATTR_VALUE_TYPE_OBJECT_ID) + { + // currently we only support reference on OID, not list + SWSS_LOG_THROW("attr value type %d, not supported yet, FIXME", + revgraph->attrmetadata->attrvaluetype); + } + + if (attr->getOid() != currentObj->getVid()) + { + // VID is not matching, skip this attribute + continue; + } + + // we found object and attribute that is using current VID, + // it's possible this VID is used on multiple attributes on the + // same object + + SWSS_LOG_INFO("found reference object: %s", obj->m_str_object_id.c_str()); + + if (revgraph->attrmetadata->iscreateonly && status == SAI_OBJECT_STATUS_NOT_PROCESSED) + { + // attribute is create only, and object was not processed + // yet this means that we need to remove object, since we + // can't break the link + + removeCurrentObjectDependencyTree(currentView, temporaryView, obj); // recursion + } + else if (revgraph->attrmetadata->iscreateandset && status == SAI_OBJECT_STATUS_NOT_PROCESSED) + { + if (revgraph->attrmetadata->allownullobjectid) + { + // we can also remove entire object here too + + SWSS_LOG_THROW("break the link is not implemented yet, FIXME"); + } + else + { + // attribute is create and set, but not allow null object id + // so probably attribute is mandatory on create, we can't break + // the link, but we can remove entire object + + removeCurrentObjectDependencyTree(currentView, temporaryView, obj); // recursion + } + } + else if (revgraph->attrmetadata->iscreateandset && status == SAI_OBJECT_STATUS_MATCHED) + { + SWSS_LOG_THROW("matched break the link is not implemented yet, FIXME"); + } + else + { + SWSS_LOG_THROW("remove on %s, obj status: %d, not supported, FIXME", revgraph->attrmetadata->attridname, status); + } + } + } + } + + removeExistingObjectFromCurrentView(currentView, temporaryView, currentObj); +} + +void ComparisonLogic::breakBeforeMake( + _In_ AsicView ¤tView, + _In_ AsicView &temporaryView, + _In_ const std::shared_ptr& currentBestMatch, // can be nullptr + _In_ const std::shared_ptr& temporaryObj) +{ + SWSS_LOG_ENTER(); + + /* + * Break Before Make rule. + * + * We can have 2 paths here: + * + * - current best match object was not found (nullptr), in case of limited + * resources, this mean that we want to remove some existing object + * before creating new, so number of existing objects will not exceed + * initial value, for example we remove acl table, then we create acl + * table. Of course this will not hold true, if temporary number of + * objects of given type is greater than current existing objects. + * + * - current best match was found, but perform Object Set Transition + * operation failed, so we can't move current object attributes to + * temporary object attributes, because for example some are create only. + */ + + if (!m_breakConfig->shouldBreakBeforeMake(temporaryObj->getObjectType())) + { + // skip object if not in break config + + return; + } + + if (currentBestMatch == nullptr) + { + // it can happen that current best match is not found for temporary + // object then we need to find best object for removal from existing + // ones the most suitable should be the one with most same attributes, + // since maybe only one read only attribute has been changed, and this + // will automatically result in null best match + + auto bcf = std::make_shared(currentView, temporaryView, m_switch); + + std::shared_ptr similarBestMatch = bcf->findSimilarBestMatch(temporaryObj); + + if (similarBestMatch == nullptr) + { + SWSS_LOG_WARN("similar best match is null, not removing"); + return; + } + + removeCurrentObjectDependencyTree(currentView, temporaryView, similarBestMatch); + + return; + } + + removeCurrentObjectDependencyTree(currentView, temporaryView, currentBestMatch); +} + void ComparisonLogic::checkSwitch( _In_ const AsicView ¤tView, _In_ const AsicView &temporaryView) diff --git a/syncd/ComparisonLogic.h b/syncd/ComparisonLogic.h index 81aa4a38f..869e12912 100644 --- a/syncd/ComparisonLogic.h +++ b/syncd/ComparisonLogic.h @@ -9,6 +9,7 @@ extern "C"{ #include "SaiSwitch.h" #include "VirtualOidTranslator.h" #include "NotificationHandler.h" +#include "BreakConfig.h" #include @@ -24,7 +25,8 @@ namespace syncd _In_ std::shared_ptr handler, _In_ std::set initViewRemovedVids, _In_ std::shared_ptr current, - _In_ std::shared_ptr temp); + _In_ std::shared_ptr temp, + _In_ std::shared_ptr breakConfig); virtual ~ComparisonLogic();; @@ -138,6 +140,17 @@ namespace syncd _In_ const std::shared_ptr& temporaryObj, _In_ bool performTransition); + void breakBeforeMake( + _In_ AsicView ¤tView, + _In_ AsicView &temporaryView, + _In_ const std::shared_ptr& currentBestMatch, + _In_ const std::shared_ptr& temporaryObj); + + void removeCurrentObjectDependencyTree( + _In_ AsicView ¤tView, + _In_ AsicView &temporaryView, + _In_ const std::shared_ptr& currentObj); + void processObjectForViewTransition( _In_ AsicView& currentView, _In_ AsicView& temporaryView, @@ -226,5 +239,7 @@ namespace syncd std::shared_ptr m_temp; std::shared_ptr m_handler; + + std::shared_ptr m_breakConfig; }; } diff --git a/syncd/Makefile.am b/syncd/Makefile.am index 320ad1aea..12d9740ed 100644 --- a/syncd/Makefile.am +++ b/syncd/Makefile.am @@ -17,6 +17,8 @@ endif noinst_LIBRARIES = libSyncd.a libSyncdRequestShutdown.a libSyncd_a_SOURCES = \ Syncd.cpp \ + BreakConfig.cpp \ + BreakConfigParser.cpp \ RedisClient.cpp \ WarmRestartTable.cpp \ RequestShutdownCommandLineOptions.cpp \ diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index 0a4771816..d1df14c1c 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -8,6 +8,7 @@ #include "RequestShutdown.h" #include "WarmRestartTable.h" #include "ContextConfigContainer.h" +#include "BreakConfigParser.h" #include "sairediscommon.h" @@ -134,6 +135,8 @@ Syncd::Syncd( abort(); } + m_breakConfig = BreakConfigParser::parseBreakConfig(m_commandLineOptions->m_breakConfig); + SWSS_LOG_NOTICE("syncd started"); } @@ -2504,7 +2507,25 @@ sai_status_t Syncd::processNotifySyncd( SWSS_LOG_WARN("syncd received APPLY VIEW, will translate"); - sai_status_t status = applyView(); + sai_status_t status; + + try + { + status = applyView(); + } + catch(...) + { + /* + * If apply view will fail with exception, try to send fail + * response to sairedis, since later there can be switch shutdown + * notification sent, and it will be synchronized with mutex, and + * it will not be processed until get response timeout will hit. + */ + + sendNotifyResponse(SAI_STATUS_FAILURE); + + throw; + } sendNotifyResponse(status); @@ -2710,7 +2731,7 @@ sai_status_t Syncd::applyView() auto current = std::make_shared(currentMap.at(switchVid)); auto temp = std::make_shared(temporaryMap.at(switchVid)); - auto cl = std::make_shared(m_vendorSai, sw, m_handler, m_initViewRemovedVidSet, current, temp); + auto cl = std::make_shared(m_vendorSai, sw, m_handler, m_initViewRemovedVidSet, current, temp, m_breakConfig); cl->compareViews(); diff --git a/syncd/Syncd.h b/syncd/Syncd.h index 561f95797..d94f90a3f 100644 --- a/syncd/Syncd.h +++ b/syncd/Syncd.h @@ -14,6 +14,7 @@ #include "RedisVidIndexGenerator.h" #include "RequestShutdown.h" #include "ContextConfig.h" +#include "BreakConfig.h" #include "meta/SaiAttributeList.h" @@ -454,5 +455,7 @@ namespace syncd std::shared_ptr m_virtualObjectIdManager; std::shared_ptr m_contextConfig; + + std::shared_ptr m_breakConfig; }; } diff --git a/syncd/VendorSai.cpp b/syncd/VendorSai.cpp index 9008e9af3..ed16bec78 100644 --- a/syncd/VendorSai.cpp +++ b/syncd/VendorSai.cpp @@ -164,9 +164,9 @@ sai_status_t VendorSai::remove( return SAI_STATUS_FAILURE; } - if (!info->create) + if (!info->remove) { - SWSS_LOG_ERROR("object type %s has no create method", + SWSS_LOG_ERROR("object type %s has no remove method", sai_serialize_object_type(objectType).c_str()); return SAI_STATUS_FAILURE; @@ -204,9 +204,9 @@ sai_status_t VendorSai::set( return SAI_STATUS_FAILURE; } - if (!info->create) + if (!info->set) { - SWSS_LOG_ERROR("object type %s has no create method", + SWSS_LOG_ERROR("object type %s has no set method", sai_serialize_object_type(objectType).c_str()); return SAI_STATUS_FAILURE; @@ -252,9 +252,9 @@ sai_status_t VendorSai::get( return SAI_STATUS_FAILURE; } - if (!info->create) + if (!info->get) { - SWSS_LOG_ERROR("object type %s has no create method", + SWSS_LOG_ERROR("object type %s has no get method", sai_serialize_object_type(objectType).c_str()); return SAI_STATUS_FAILURE; diff --git a/tests/BCM56850.pl b/tests/BCM56850.pl index 2219448e9..5d5e6a2a7 100755 --- a/tests/BCM56850.pl +++ b/tests/BCM56850.pl @@ -546,8 +546,16 @@ sub test_brcm_query_object_type_get_availability play "query_object_type_get_availability.rec"; } +sub test_brcm_acl_limit +{ + fresh_start("-b", "$utils::DIR/bbm.ini", "-p", "$utils::DIR/vsprofile_acl_limit.ini"); + + play "acl_limit.rec"; +} + # RUN TESTS +test_brcm_acl_limit; test_sync_brcm_warm_boot_port_remove; test_brcm_warm_boot_port_remove; test_brcm_warm_boot_port_create; diff --git a/tests/BCM56850/bbm.ini b/tests/BCM56850/bbm.ini new file mode 100644 index 000000000..f36bfbba8 --- /dev/null +++ b/tests/BCM56850/bbm.ini @@ -0,0 +1 @@ +SAI_OBJECT_TYPE_ACL_TABLE diff --git a/tests/BCM56850/limits.ini b/tests/BCM56850/limits.ini new file mode 100644 index 000000000..1ea818a1d --- /dev/null +++ b/tests/BCM56850/limits.ini @@ -0,0 +1 @@ +SAI_OBJECT_TYPE_ACL_TABLE=3 diff --git a/tests/BCM56850/vsprofile.ini b/tests/BCM56850/vsprofile.ini index a1620d6f2..a53fb9dda 100644 --- a/tests/BCM56850/vsprofile.ini +++ b/tests/BCM56850/vsprofile.ini @@ -1,6 +1,7 @@ SAI_WARM_BOOT_READ_FILE=./sai_warmboot.bin SAI_WARM_BOOT_WRITE_FILE=./sai_warmboot.bin SAI_VS_SWITCH_TYPE=SAI_VS_SWITCH_TYPE_BCM56850 -SAI_VS_INTERFACE_LANE_MAP_FILE=brcm/lanemap.ini -#SAI_VS_INTERFACE_LANE_MAP_FILE=brcm/lane66.ini +SAI_VS_INTERFACE_LANE_MAP_FILE=BCM56850/lanemap.ini +#SAI_VS_INTERFACE_LANE_MAP_FILE=BCM56850/lane66.ini #SAI_VS_HOSTIF_USE_TAP_DEVICE=true +#SAI_VS_RESOURCE_LIMITER_FILE=BCM56850/limits.ini diff --git a/tests/BCM56850/vsprofile_acl_limit.ini b/tests/BCM56850/vsprofile_acl_limit.ini new file mode 100644 index 000000000..e2b0880ed --- /dev/null +++ b/tests/BCM56850/vsprofile_acl_limit.ini @@ -0,0 +1,7 @@ +SAI_WARM_BOOT_READ_FILE=./sai_warmboot.bin +SAI_WARM_BOOT_WRITE_FILE=./sai_warmboot.bin +SAI_VS_SWITCH_TYPE=SAI_VS_SWITCH_TYPE_BCM56850 +SAI_VS_INTERFACE_LANE_MAP_FILE=BCM56850/lanemap.ini +#SAI_VS_INTERFACE_LANE_MAP_FILE=brcm/lane66.ini +#SAI_VS_HOSTIF_USE_TAP_DEVICE=true +SAI_VS_RESOURCE_LIMITER_FILE=BCM56850/limits.ini diff --git a/tests/aspell.en.pws b/tests/aspell.en.pws index 80b10f6f6..bb6bdbf37 100644 --- a/tests/aspell.en.pws +++ b/tests/aspell.en.pws @@ -2,6 +2,7 @@ personal_ws-1.1 en 0 acl ACL ACLs +apache api API apis @@ -94,8 +95,10 @@ hardcoded hasEqualAttribute hostif HSV +http https hw +hwinfo ifdef IFF ifindex @@ -280,5 +283,6 @@ vxlan VXLAN workaroung WRED +www xoff xon diff --git a/tests/utils.pm b/tests/utils.pm index 4c5d76b4d..e40afc7ba 100644 --- a/tests/utils.pm +++ b/tests/utils.pm @@ -48,7 +48,7 @@ sub flush_redis sub start_syncd { print color('bright_blue') . "Starting syncd" . color('reset') . "\n"; - `./vssyncd -SUu -p "$DIR/vsprofile.ini" >/dev/null 2>/dev/null &`; + `./vssyncd -SUu -p "$DIR/vsprofile.ini" @_ >/dev/null 2>/dev/null &`; } sub start_syncd_warm @@ -143,7 +143,7 @@ sub fresh_start kill_syncd; flush_redis; - start_syncd; + start_syncd @_; } sub sync_fresh_start diff --git a/vslib/inc/ResourceLimiter.h b/vslib/inc/ResourceLimiter.h new file mode 100644 index 000000000..700addbaf --- /dev/null +++ b/vslib/inc/ResourceLimiter.h @@ -0,0 +1,44 @@ +#pragma once + +extern "C" { +#include "sai.h" +} + +#include + +namespace saivs +{ + class ResourceLimiter + { + public: + + constexpr static uint32_t DEFAULT_SWITCH_INDEX = 0; + + public: + + ResourceLimiter( + _In_ uint32_t switchIndex); + + virtual ~ResourceLimiter() = default; + + public: + + size_t getObjectTypeLimit( + _In_ sai_object_type_t objectType) const; + + void setObjectTypeLimit( + _In_ sai_object_type_t objectType, + _In_ size_t limit); + + void removeObjectTypeLimit( + _In_ sai_object_type_t objectType); + + void clearLimits(); + + private: + + uint32_t m_switchIndex; + + std::map m_objectTypeLimits; + }; +} diff --git a/vslib/inc/ResourceLimiterContainer.h b/vslib/inc/ResourceLimiterContainer.h new file mode 100644 index 000000000..a63ebc358 --- /dev/null +++ b/vslib/inc/ResourceLimiterContainer.h @@ -0,0 +1,35 @@ +#pragma once + +#include "ResourceLimiter.h" + +#include + +namespace saivs +{ + class ResourceLimiterContainer + { + public: + + ResourceLimiterContainer() = default; + + virtual ~ResourceLimiterContainer() = default; + + public: + + void insert( + _In_ uint32_t switchIndex, + _In_ std::shared_ptr rl); + + void remove( + _In_ uint32_t switchIndex); + + std::shared_ptr getResourceLimiter( + _In_ uint32_t switchIndex) const; + + void clear(); + + private: + + std::map> m_container; + }; +} diff --git a/vslib/inc/ResourceLimiterParser.h b/vslib/inc/ResourceLimiterParser.h new file mode 100644 index 000000000..ba39ce6b6 --- /dev/null +++ b/vslib/inc/ResourceLimiterParser.h @@ -0,0 +1,41 @@ +#pragma once + +#include "ResourceLimiterContainer.h" + +#include +#include + +namespace saivs +{ + class ResourceLimiterParser + { + public: + + ResourceLimiterParser() = delete; + + ~ResourceLimiterParser() = delete; + + public: + + static std::shared_ptr parseFromFile( + _In_ const char* fileName); + + private: + + static void parseLineWithIndex( + _In_ std::shared_ptr container, + _In_ const std::vector& tokens, + _In_ const std::string& strLimit); + + static void parseLineWithNoIndex( + _In_ std::shared_ptr container, + _In_ const std::vector& tokens, + _In_ const std::string& strLimit); + + static void parse( + _In_ std::shared_ptr container, + _In_ uint32_t switchIndex, + _In_ const std::string& strObjectType, + _In_ const std::string& strLimit); + }; +} diff --git a/vslib/inc/Sai.h b/vslib/inc/Sai.h index 7a6ec637b..9a809e569 100644 --- a/vslib/inc/Sai.h +++ b/vslib/inc/Sai.h @@ -4,6 +4,7 @@ #include "LaneMapContainer.h" #include "EventQueue.h" #include "EventPayloadNotification.h" +#include "ResourceLimiterContainer.h" #include "meta/Meta.h" @@ -413,5 +414,7 @@ namespace saivs const char *m_warm_boot_write_file; std::shared_ptr m_laneMapContainer; + + std::shared_ptr m_resourceLimiterContainer; }; } diff --git a/vslib/inc/SwitchConfig.h b/vslib/inc/SwitchConfig.h index bec0541a9..652428e21 100644 --- a/vslib/inc/SwitchConfig.h +++ b/vslib/inc/SwitchConfig.h @@ -2,6 +2,7 @@ #include "LaneMap.h" #include "EventQueue.h" +#include "ResourceLimiter.h" #include #include @@ -76,5 +77,7 @@ namespace saivs std::shared_ptr m_laneMap; std::shared_ptr m_eventQueue; + + std::shared_ptr m_resourceLimiter; }; } diff --git a/vslib/inc/saivs.h b/vslib/inc/saivs.h index 8c5be3dac..2f612746f 100644 --- a/vslib/inc/saivs.h +++ b/vslib/inc/saivs.h @@ -23,6 +23,16 @@ extern "C" { */ #define SAI_KEY_VS_INTERFACE_LANE_MAP_FILE "SAI_VS_INTERFACE_LANE_MAP_FILE" +/** + * @def SAI_KEY_VS_RESOURCE_LIMITER_FILE + * + * File with resource limitations for object type create. + * + * Example: + * SAI_OBJECT_TYPE_ACL_TABLE=3 + */ +#define SAI_KEY_VS_RESOURCE_LIMITER_FILE "SAI_VS_RESOURCE_LIMITER_FILE" + /** * @def SAI_KEY_VS_HOSTIF_USE_TAP_DEVICE * diff --git a/vslib/src/Makefile.am b/vslib/src/Makefile.am index 8f2208247..2a14a44e7 100644 --- a/vslib/src/Makefile.am +++ b/vslib/src/Makefile.am @@ -13,6 +13,9 @@ libSaiVS_a_SOURCES = \ ../../lib/src/NotificationFdbEvent.cpp \ ../../lib/src/Notification.cpp \ ../../lib/src/NotificationPortStateChange.cpp \ + ResourceLimiter.cpp \ + ResourceLimiterContainer.cpp \ + ResourceLimiterParser.cpp \ EventPayloadNotification.cpp \ EventPayloadNetLinkMsg.cpp \ SaiEventQueue.cpp \ diff --git a/vslib/src/ResourceLimiter.cpp b/vslib/src/ResourceLimiter.cpp new file mode 100644 index 000000000..158048a43 --- /dev/null +++ b/vslib/src/ResourceLimiter.cpp @@ -0,0 +1,60 @@ +#include "ResourceLimiter.h" + +#include "swss/logger.h" +#include "meta/sai_serialize.h" + +using namespace saivs; + +ResourceLimiter::ResourceLimiter( + _In_ uint32_t switchIndex): + m_switchIndex(switchIndex) +{ + SWSS_LOG_ENTER(); + + // empty +} + +size_t ResourceLimiter::getObjectTypeLimit( + _In_ sai_object_type_t objectType) const +{ + SWSS_LOG_ENTER(); + + auto it = m_objectTypeLimits.find(objectType); + + if (it != m_objectTypeLimits.end()) + { + return it->second; + } + + // default limit is maximum + + return SIZE_MAX; +} + +void ResourceLimiter::setObjectTypeLimit( + _In_ sai_object_type_t objectType, + _In_ size_t limit) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_INFO("setting %s limit to %zu", + sai_serialize_object_type(objectType).c_str(), + limit); + + m_objectTypeLimits[objectType] = limit; +} + +void ResourceLimiter::removeObjectTypeLimit( + _In_ sai_object_type_t objectType) +{ + SWSS_LOG_ENTER(); + + m_objectTypeLimits[objectType] = SIZE_MAX; +} + +void ResourceLimiter::clearLimits() +{ + SWSS_LOG_ENTER(); + + m_objectTypeLimits.clear(); +} diff --git a/vslib/src/ResourceLimiterContainer.cpp b/vslib/src/ResourceLimiterContainer.cpp new file mode 100644 index 000000000..9e67f25ce --- /dev/null +++ b/vslib/src/ResourceLimiterContainer.cpp @@ -0,0 +1,50 @@ +#include "ResourceLimiterContainer.h" + +#include "swss/logger.h" + +using namespace saivs; + +void ResourceLimiterContainer::insert( + _In_ uint32_t switchIndex, + _In_ std::shared_ptr rl) +{ + SWSS_LOG_ENTER(); + + m_container[switchIndex] = rl; +} + +void ResourceLimiterContainer::remove( + _In_ uint32_t switchIndex) +{ + SWSS_LOG_ENTER(); + + auto it = m_container.find(switchIndex); + + if (it != m_container.end()) + { + m_container.erase(it); + } +} + +std::shared_ptr ResourceLimiterContainer::getResourceLimiter( + _In_ uint32_t switchIndex) const +{ + SWSS_LOG_ENTER(); + + auto it = m_container.find(switchIndex); + + if (it != m_container.end()) + { + return it->second; + } + + return nullptr; +} + +void ResourceLimiterContainer::clear() +{ + SWSS_LOG_ENTER(); + + m_container.clear(); +} + diff --git a/vslib/src/ResourceLimiterParser.cpp b/vslib/src/ResourceLimiterParser.cpp new file mode 100644 index 000000000..88bcc60ec --- /dev/null +++ b/vslib/src/ResourceLimiterParser.cpp @@ -0,0 +1,163 @@ +#include "ResourceLimiterParser.h" + +#include "swss/logger.h" +#include "swss/tokenize.h" + +#include "meta/sai_serialize.h" + +#include + +using namespace saivs; + +std::shared_ptr ResourceLimiterParser::parseFromFile( + _In_ const char* fileName) +{ + SWSS_LOG_ENTER(); + + if (fileName == nullptr) + { + SWSS_LOG_NOTICE("file name is NULL, returning empty limiter"); + + return std::make_shared(); + } + + std::string file(fileName); + + std::ifstream ifs(file); + + if (!ifs.is_open()) + { + SWSS_LOG_WARN("failed to open resource limiter file: %s", file.c_str()); + + return std::make_shared(); + } + + SWSS_LOG_NOTICE("loading resource limits from: %s", file.c_str()); + + std::string line; + + auto container = std::make_shared(); + + while (getline(ifs, line)) + { + /* + * line can be in 2 forms: + * + * SAI_OBJECT_TYPE_XXX=limit + * N:SAI_OBJECT_TYPE_XXX=limit + * + * where N is switchIndex (0..255) - SAI_VS_SWITCH_INDEX_MAX + * if N is not specified then zero (0) is assumed + */ + + if (line.size() > 0 && (line[0] == '#' || line[0] == ';')) + { + continue; + } + + SWSS_LOG_INFO("line: %s", line.c_str()); + + auto toks = swss::tokenize(line, '='); + + if (toks.size() != 2) + { + SWSS_LOG_ERROR("expected 2 tokens, got: %zu on line: %s", toks.size(), line.c_str()); + continue; + } + + auto tokens = swss::tokenize(toks.at(0), ':'); + + if (tokens.size() == 2) + { + parseLineWithIndex(container, tokens, toks.at(1)); + } + else if (tokens.size() == 1) + { + parseLineWithNoIndex(container, tokens, toks.at(1)); + } + else + { + SWSS_LOG_ERROR("expected 1 or 2 tokens in line %s, got %zu", line.c_str(), tokens.size()); + } + } + + return container; +} + +void ResourceLimiterParser::parseLineWithIndex( + _In_ std::shared_ptr container, + _In_ const std::vector& tokens, + _In_ const std::string& strLimit) +{ + SWSS_LOG_ENTER(); + + auto swidx = tokens.at(0); + auto strObjectType = tokens.at(1); + + uint32_t switchIndex; + + if (sscanf(swidx.c_str(), "%u", & switchIndex) != 1) + { + SWSS_LOG_ERROR("failed to parse switchIndex: %s", swidx.c_str()); + return; + } + + parse(container, switchIndex, strObjectType, strLimit); +} + +void ResourceLimiterParser::parseLineWithNoIndex( + _In_ std::shared_ptr container, + _In_ const std::vector& tokens, + _In_ const std::string& strLimit) +{ + SWSS_LOG_ENTER(); + + auto strObjectType = tokens.at(0); + + parse(container, ResourceLimiter::DEFAULT_SWITCH_INDEX, strObjectType, strLimit); +} + +void ResourceLimiterParser::parse( + _In_ std::shared_ptr container, + _In_ uint32_t switchIndex, + _In_ const std::string& strObjectType, + _In_ const std::string& strLimit) +{ + SWSS_LOG_ENTER(); + + sai_object_type_t objectType; + + try + { + sai_deserialize_object_type(strObjectType, objectType); + } + catch(const std::exception& e) + { + SWSS_LOG_ERROR("failed to deserialize '%s' as object type: %s", strObjectType.c_str(), e.what()); + return; + } + + size_t limit; + + if (sscanf(strLimit.c_str(), "%zu", &limit) != 1) + { + SWSS_LOG_ERROR("failed to parse '%s' as limit", strLimit.c_str()); + return; + } + + auto limiter = container->getResourceLimiter(switchIndex); + + if (limiter == nullptr) + { + limiter = std::make_shared(switchIndex); + + container->insert(switchIndex, limiter); + } + + SWSS_LOG_NOTICE("adding limit on switch index %u, %s = %zu", + switchIndex, + sai_serialize_object_type(objectType).c_str(), + limit); + + limiter->setObjectTypeLimit(objectType, limit); +} diff --git a/vslib/src/Sai.cpp b/vslib/src/Sai.cpp index 1699fd1c8..d46a52045 100644 --- a/vslib/src/Sai.cpp +++ b/vslib/src/Sai.cpp @@ -6,6 +6,7 @@ #include "LaneMapFileParser.h" #include "HostInterfaceInfo.h" #include "SwitchConfigContainer.h" +#include "ResourceLimiterParser.h" #include "swss/logger.h" @@ -111,6 +112,10 @@ sai_status_t Sai::initialize( m_laneMapContainer = LaneMapFileParser::parseLaneMapFile(laneMapFile); + auto *resourceLimiterFile = service_method_table->profile_get_value(0, SAI_KEY_VS_RESOURCE_LIMITER_FILE); + + m_resourceLimiterContainer = ResourceLimiterParser::parseFromFile(resourceLimiterFile); + auto boot_type = service_method_table->profile_get_value(0, SAI_KEY_BOOT_TYPE); m_warm_boot_read_file = service_method_table->profile_get_value(0, SAI_KEY_WARM_BOOT_READ_FILE); m_warm_boot_write_file = service_method_table->profile_get_value(0, SAI_KEY_WARM_BOOT_WRITE_FILE); @@ -148,9 +153,14 @@ sai_status_t Sai::initialize( sc->m_useTapDevice = useTapDevice; sc->m_laneMap = m_laneMapContainer->getLaneMap(sc->m_switchIndex); sc->m_eventQueue = m_eventQueue; + sc->m_resourceLimiter = m_resourceLimiterContainer->getResourceLimiter(sc->m_switchIndex); auto scc = std::make_shared(); + // TODO add support for multiple switches, (global context?) and config context will need + // to be passed over SAI_KEY_ service method table, and here we need to load them + // we also need global context value for those switches (VirtualSwitchSaiInterface/RealObjectIdManager) + scc->insert(sc); // most important diff --git a/vslib/src/SwitchState.cpp b/vslib/src/SwitchState.cpp index fb1f59f61..ed2b2bb2e 100644 --- a/vslib/src/SwitchState.cpp +++ b/vslib/src/SwitchState.cpp @@ -55,6 +55,12 @@ SwitchState::SwitchState( 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() diff --git a/vslib/src/SwitchStateBase.cpp b/vslib/src/SwitchStateBase.cpp index 94e050d63..0713ae58c 100644 --- a/vslib/src/SwitchStateBase.cpp +++ b/vslib/src/SwitchStateBase.cpp @@ -168,6 +168,20 @@ sai_status_t SwitchStateBase::create_internal( auto &objectHash = m_objectHash.at(object_type); + if (m_switchConfig->m_resourceLimiter) + { + size_t limit = m_switchConfig->m_resourceLimiter->getObjectTypeLimit(object_type); + + if (objectHash.size() >= limit) + { + SWSS_LOG_ERROR("too many %s, created %zu is resource limit", + sai_serialize_object_type(object_type).c_str(), + limit); + + return SAI_STATUS_INSUFFICIENT_RESOURCES; + } + } + auto it = objectHash.find(serializedObjectId); if (object_type != SAI_OBJECT_TYPE_SWITCH) diff --git a/vslib/src/VirtualSwitchSaiInterface.cpp b/vslib/src/VirtualSwitchSaiInterface.cpp index 225c0e207..e080a9090 100644 --- a/vslib/src/VirtualSwitchSaiInterface.cpp +++ b/vslib/src/VirtualSwitchSaiInterface.cpp @@ -29,7 +29,7 @@ VirtualSwitchSaiInterface::VirtualSwitchSaiInterface( { SWSS_LOG_ENTER(); - m_realObjectIdManager = std::make_shared(0, scc); + m_realObjectIdManager = std::make_shared(0, scc); // TODO fix global context m_switchConfigContainer = scc; }