diff --git a/lib/RedisRemoteSaiInterface.cpp b/lib/RedisRemoteSaiInterface.cpp index 4c0247109..08ffb1939 100644 --- a/lib/RedisRemoteSaiInterface.cpp +++ b/lib/RedisRemoteSaiInterface.cpp @@ -568,6 +568,8 @@ sai_status_t RedisRemoteSaiInterface::notifyCounterGroupOperations( std::string key((const char*)flexCounterGroupParam->counter_group_name.list, flexCounterGroupParam->counter_group_name.count); emplaceStrings(POLL_INTERVAL_FIELD, flexCounterGroupParam->poll_interval, entries); + emplaceStrings(BULK_CHUNK_SIZE_FIELD, flexCounterGroupParam->bulk_chunk_size, entries); + emplaceStrings(BULK_CHUNK_SIZE_PER_PREFIX_FIELD, flexCounterGroupParam->bulk_chunk_size_per_prefix, entries); emplaceStrings(STATS_MODE_FIELD, flexCounterGroupParam->stats_mode, entries); emplaceStrings(flexCounterGroupParam->plugin_name, flexCounterGroupParam->plugins, entries); emplaceStrings(FLEX_COUNTER_STATUS_FIELD, flexCounterGroupParam->operation, entries); diff --git a/lib/sairedis.h b/lib/sairedis.h index 56a21bd4d..3481de3df 100644 --- a/lib/sairedis.h +++ b/lib/sairedis.h @@ -140,6 +140,20 @@ typedef struct _sai_redis_flex_counter_group_parameter_t */ sai_s8_list_t plugins; + /** + * @brief The bulk chunk size of the counter group + * + * It should be a number representing the bulk chunk size. + */ + sai_s8_list_t bulk_chunk_size; + + /** + * @brief The bulk counter prefix map the counter group + * + * It should be a string representing bulk chunk size of each sub counter group. + */ + sai_s8_list_t bulk_chunk_size_per_prefix; + } sai_redis_flex_counter_group_parameter_t; typedef struct _sai_redis_flex_counter_parameter_t diff --git a/syncd/FlexCounter.cpp b/syncd/FlexCounter.cpp index f907f4f07..54c3965b5 100644 --- a/syncd/FlexCounter.cpp +++ b/syncd/FlexCounter.cpp @@ -41,8 +41,9 @@ const std::map FlexCounter::m_plugIn2CounterType = { {TUNNEL_PLUGIN_FIELD, COUNTER_TYPE_TUNNEL}, {FLOW_COUNTER_PLUGIN_FIELD, COUNTER_TYPE_FLOW}}; -BaseCounterContext::BaseCounterContext(const std::string &name): -m_name(name) +BaseCounterContext::BaseCounterContext(const std::string &name, const std::string &instance): +m_name(name), +m_instanceId(instance) { SWSS_LOG_ENTER(); } @@ -73,6 +74,20 @@ void BaseCounterContext::setNoDoubleCheckBulkCapability( no_double_check_bulk_capability = noDoubleCheckBulkCapability; } +void BaseCounterContext::setBulkChunkSize( + _In_ uint32_t bulkChunkSize) +{ + SWSS_LOG_ENTER(); + default_bulk_chunk_size = bulkChunkSize; +} + +void BaseCounterContext::setBulkChunkSizePerPrefix( + _In_ const std::string& bulkChunkSizePerPrefix) +{ + SWSS_LOG_ENTER(); + m_bulkChunkSizePerPrefix = bulkChunkSizePerPrefix; +} + template struct CounterIds @@ -143,6 +158,8 @@ struct BulkStatsContext std::vector counter_ids; std::vector object_statuses; std::vector counters; + std::string name; + uint32_t default_bulk_chunk_size; }; // TODO: use if const expression when cpp17 is supported @@ -417,16 +434,19 @@ void deserializeAttr( template class CounterContext : public BaseCounterContext { + std::map m_counterChunkSizeMapFromPrefix; + public: typedef CounterIds CounterIdsType; typedef BulkStatsContext BulkContextType; CounterContext( _In_ const std::string &name, + _In_ const std::string &instance, _In_ sai_object_type_t object_type, _In_ sairedis::SaiInterface *vendor_sai, _In_ sai_stats_mode_t &stats_mode): - BaseCounterContext(name), m_objectType(object_type), m_vendorSai(vendor_sai), m_groupStatsMode(stats_mode) + BaseCounterContext(name, instance), m_objectType(object_type), m_vendorSai(vendor_sai), m_groupStatsMode(stats_mode) { SWSS_LOG_ENTER(); } @@ -524,12 +544,209 @@ class CounterContext : public BaseCounterContext } m_objectIdsMap.emplace(vid, counter_data); } - else + else if (m_counterChunkSizeMapFromPrefix.empty()) { std::sort(supportedIds.begin(), supportedIds.end()); - auto bulkContext = getBulkStatsContext(supportedIds); + auto bulkContext = getBulkStatsContext(supportedIds, "default", default_bulk_chunk_size); addBulkStatsContext(vid, rid, supportedIds, *bulkContext.get()); } + else + { + std::map> counter_prefix_map; + std::vector default_partition; + mapCountersByPrefix(supportedIds, counter_prefix_map, default_partition); + + for (auto &counterPrefix : counter_prefix_map) + { + std::sort(counterPrefix.second.begin(), counterPrefix.second.end()); + auto bulkContext = getBulkStatsContext(counterPrefix.second, counterPrefix.first, m_counterChunkSizeMapFromPrefix[counterPrefix.first]); + addBulkStatsContext(vid, rid, counterPrefix.second, *bulkContext.get()); + } + + std::sort(default_partition.begin(), default_partition.end()); + auto bulkContext = getBulkStatsContext(default_partition, "default", default_bulk_chunk_size); + addBulkStatsContext(vid, rid, default_partition, *bulkContext.get()); + } + } + + void parseCounterPrefixConfigString( + _In_ const std::string& prefixConfigString) + { + SWSS_LOG_ENTER(); + + m_counterChunkSizeMapFromPrefix.clear(); + + if (!prefixConfigString.empty()) + { + auto tokens = swss::tokenize(prefixConfigString, ';'); + + for (auto &token: tokens) + { + auto counter_name_bulk_size = swss::tokenize(token, ':'); + SWSS_LOG_INFO("New partition %s bulk chunk size %s", counter_name_bulk_size[0].c_str(), counter_name_bulk_size[1].c_str()); + m_counterChunkSizeMapFromPrefix[counter_name_bulk_size[0]] = stoi(counter_name_bulk_size[1]); + } + } + } + + void mapCountersByPrefix( + _In_ const std::vector& supportedIds, + _Out_ std::map> &counter_prefix_map, + _Out_ std::vector &default_partition, + _In_ bool log=false) + { + SWSS_LOG_ENTER(); + + default_partition.clear(); + for (auto &counter : supportedIds) + { + std::string counterStr = serializeStat(counter); + bool found = false; + for (auto searchRef: m_counterChunkSizeMapFromPrefix) + { + if (!searchRef.first.empty() && counterStr.find(searchRef.first) != std::string::npos) + { + found = true; + counter_prefix_map[searchRef.first].push_back(counter); + if (log) + { + SWSS_LOG_INFO("Put counter %s to partition %s", counterStr.c_str(), searchRef.first.c_str()); + } + break; + } + } + if (!found) + { + default_partition.push_back(counter); + + if (log) + { + SWSS_LOG_INFO("Put counter %s to the default partition", counterStr.c_str()); + } + } + } + } + + void setBulkChunkSize( + _In_ uint32_t bulkChunkSize) override + { + SWSS_LOG_ENTER(); + default_bulk_chunk_size = bulkChunkSize; + SWSS_LOG_INFO("Bulk chunk size updatd to %u", bulkChunkSize); + + for (auto &bulkStatsContext : m_bulkContexts) + { + auto const &name = (*bulkStatsContext.second.get()).name; + if (name == "default") + { + SWSS_LOG_INFO("Bulk chunk size of default updated to %u", bulkChunkSize); + (*bulkStatsContext.second.get()).default_bulk_chunk_size = default_bulk_chunk_size; + break; + } + } + } + + void setBulkChunkSizePerPrefix( + _In_ const std::string& bulkChunkSizePerPrefix) override + { + SWSS_LOG_ENTER(); + + m_bulkChunkSizePerPrefix = bulkChunkSizePerPrefix; + + parseCounterPrefixConfigString(bulkChunkSizePerPrefix); + if (m_bulkContexts.empty()) + { + return; + } + + if (m_bulkContexts.size() == 1) + { + // hasn't split + SWSS_LOG_NOTICE("Split counter IDs set by prefix for the first time %s", bulkChunkSizePerPrefix.c_str()); + auto it = m_bulkContexts.begin(); + BulkContextType &singleBulkContext = *it->second.get(); + const std::vector &allCounterIds = singleBulkContext.counter_ids; + std::map> counterChunkSizePerPrefix; + std::vector defaultPartition; + + if (m_counterChunkSizeMapFromPrefix.empty()) + { + singleBulkContext.default_bulk_chunk_size = default_bulk_chunk_size; + } + else + { + mapCountersByPrefix(allCounterIds, counterChunkSizePerPrefix, defaultPartition, true); + + for (auto &counterPrefix : counterChunkSizePerPrefix) + { + std::sort(counterPrefix.second.begin(), counterPrefix.second.end()); + auto bulkContext = getBulkStatsContext(counterPrefix.second, counterPrefix.first, m_counterChunkSizeMapFromPrefix[counterPrefix.first]); + + bulkContext.get()->counter_ids = move(counterPrefix.second); + bulkContext.get()->object_statuses.resize(singleBulkContext.object_statuses.size()); + bulkContext.get()->object_vids = singleBulkContext.object_vids; + bulkContext.get()->object_keys = singleBulkContext.object_keys; + bulkContext.get()->counters.resize(bulkContext.get()->counter_ids.size() * bulkContext.get()->object_vids.size()); + + SWSS_LOG_INFO("Re-initializing counter partition %s", counterPrefix.first.c_str()); + } + + std::sort(defaultPartition.begin(), defaultPartition.end()); + auto defaultBulkContext = getBulkStatsContext(defaultPartition, "default", default_bulk_chunk_size); + defaultBulkContext.get()->counter_ids = move(defaultPartition); + defaultBulkContext.get()->object_statuses = move(singleBulkContext.object_statuses); + defaultBulkContext.get()->object_vids = move(singleBulkContext.object_vids); + defaultBulkContext.get()->object_keys = move(singleBulkContext.object_keys); + defaultBulkContext.get()->counters.resize(defaultBulkContext.get()->counter_ids.size() * defaultBulkContext.get()->object_vids.size()); + m_bulkContexts.erase(it); + SWSS_LOG_INFO("Removed the previous default counter partition"); + } + } + else + { + SWSS_LOG_NOTICE("Update bulk chunk size only %s", bulkChunkSizePerPrefix.c_str()); + + auto counterChunkSizeMapFromPrefix = m_counterChunkSizeMapFromPrefix; + for (auto &bulkStatsContext : m_bulkContexts) + { + auto const &name = (*bulkStatsContext.second.get()).name; + + if (name == "default") + { + continue; + } + + auto const &searchRef = counterChunkSizeMapFromPrefix.find(name); + if (searchRef != counterChunkSizeMapFromPrefix.end()) + { + auto const &chunkSize = searchRef->second; + + SWSS_LOG_INFO("Reset counter prefix %s chunk size %d", name.c_str(), chunkSize); + (*bulkStatsContext.second.get()).default_bulk_chunk_size = chunkSize; + counterChunkSizeMapFromPrefix.erase(searchRef); + } + else + { + SWSS_LOG_WARN("Update bulk chunk size: bulk chunk size for prefix %s is not provided", name.c_str()); + } + } + + for (auto &it : counterChunkSizeMapFromPrefix) + { + SWSS_LOG_WARN("Update bulk chunk size: prefix %s does not exist", it.first.c_str()); + } + } + + for (auto context : m_bulkContexts) + { + SWSS_LOG_INFO("%s %s partition %s number of OIDs %d number of counter IDs %d number of counters %d", + m_name.c_str(), + m_instanceId.c_str(), + context.second.get()->name.c_str(), + context.second.get()->object_keys.size(), + context.second.get()->counter_ids.size(), + context.second.get()->counters.size()); + } } void removeObject( @@ -610,6 +827,9 @@ class CounterContext : public BaseCounterContext { return; } + + SWSS_LOG_INFO("Before running plugin %s %s", m_instanceId.c_str(), m_name.c_str()); + std::vector idStrings; idStrings.reserve(m_objectIdsMap.size()); std::transform(m_objectIdsMap.begin(), @@ -628,6 +848,8 @@ class CounterContext : public BaseCounterContext std::for_each(m_plugins.begin(), m_plugins.end(), [&] (auto &sha) { runRedisScript(counters_db, sha, idStrings, argv); }); + + SWSS_LOG_INFO("After running plugin %s %s", m_instanceId.c_str(), m_name.c_str()); } bool hasObject() const override @@ -730,20 +952,43 @@ class CounterContext : public BaseCounterContext { SWSS_LOG_ENTER(); auto statsMode = m_groupStatsMode == SAI_STATS_MODE_READ ? SAI_STATS_MODE_BULK_READ : SAI_STATS_MODE_BULK_READ_AND_CLEAR; - sai_status_t status = m_vendorSai->bulkGetStats( - SAI_NULL_OBJECT_ID, - m_objectType, - static_cast(ctx.object_keys.size()), - ctx.object_keys.data(), - static_cast(ctx.counter_ids.size()), - reinterpret_cast(ctx.counter_ids.data()), - statsMode, - ctx.object_statuses.data(), - ctx.counters.data()); - if (SAI_STATUS_SUCCESS != status) + uint32_t bulk_chunk_size = ctx.default_bulk_chunk_size; + uint32_t size = static_cast(ctx.object_keys.size()); + if (bulk_chunk_size > size || bulk_chunk_size == 0) { - SWSS_LOG_WARN("Failed to bulk get stats for %s: %u", m_name.c_str(), status); + bulk_chunk_size = size; } + uint32_t current = 0; + + SWSS_LOG_INFO("Before getting bulk %s %s %s size %u bulk chunk size %u current %u", m_instanceId.c_str(), m_name.c_str(), ctx.name.c_str(), size, bulk_chunk_size, current); + + while (current < size) + { + sai_status_t status = m_vendorSai->bulkGetStats( + SAI_NULL_OBJECT_ID, + m_objectType, + bulk_chunk_size, + ctx.object_keys.data() + current, + static_cast(ctx.counter_ids.size()), + reinterpret_cast(ctx.counter_ids.data()), + statsMode, + ctx.object_statuses.data() + current, + ctx.counters.data() + current * ctx.counter_ids.size()); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_WARN("Failed to bulk get stats for %s: %u", m_name.c_str(), status); + } + current += bulk_chunk_size; + + SWSS_LOG_INFO("After getting bulk %s %s %s index %u(advanced to %u) bulk chunk size %u", m_instanceId.c_str(), m_name.c_str(), ctx.name.c_str(), current - bulk_chunk_size, current, bulk_chunk_size); + + if (size - current < bulk_chunk_size) + { + bulk_chunk_size = size - current; + } + } + + auto time_stamp = std::chrono::steady_clock::now().time_since_epoch().count(); std::vector values; for (size_t i = 0; i < ctx.object_keys.size(); i++) @@ -759,13 +1004,18 @@ class CounterContext : public BaseCounterContext { values.emplace_back(serializeStat(ctx.counter_ids[j]), std::to_string(ctx.counters[i * ctx.counter_ids.size() + j])); } + values.emplace_back(m_instanceId + "_time_stamp", std::to_string(time_stamp)); countersTable.set(sai_serialize_object_id(vid), values, ""); values.clear(); } + + SWSS_LOG_INFO("After pushing db %s %s %s", m_instanceId.c_str(), m_name.c_str(), ctx.name.c_str()); } auto getBulkStatsContext( - _In_ const std::vector& counterIds) + _In_ const std::vector& counterIds, + _In_ const std::string& name, + _In_ uint32_t bulk_chunk_size=0) { SWSS_LOG_ENTER(); auto iter = m_bulkContexts.find(counterIds); @@ -774,7 +1024,10 @@ class CounterContext : public BaseCounterContext return iter->second; } + SWSS_LOG_NOTICE("Create bulk stat context %s %s %s", m_instanceId.c_str(), m_name.c_str(), name.c_str()); auto ret = m_bulkContexts.emplace(counterIds, std::make_shared()); + (*ret.first->second.get()).name = name; + (*ret.first->second.get()).default_bulk_chunk_size = bulk_chunk_size; return ret.first->second; } @@ -825,7 +1078,15 @@ class CounterContext : public BaseCounterContext ctx.counters.resize(ctx.counter_ids.size() * ctx.object_keys.size()); ctx.object_statuses.pop_back(); } - break; + if (m_counterChunkSizeMapFromPrefix.empty()) + { + break; + } + else + { + // There can be more than one bulk context containing the VID when the per counter ID bulk chunk size is configured + continue; + } } return found; @@ -968,10 +1229,11 @@ class AttrContext : public CounterContext typedef CounterContext Base; AttrContext( _In_ const std::string &name, + _In_ const std::string &instance, _In_ sai_object_type_t object_type, _In_ sairedis::SaiInterface *vendor_sai, _In_ sai_stats_mode_t &stats_mode): - CounterContext(name, object_type, vendor_sai, stats_mode) + CounterContext(name, instance, object_type, vendor_sai, stats_mode) { SWSS_LOG_ENTER(); } @@ -1055,9 +1317,10 @@ class DashMeterCounterContext : public BaseCounterContext public: DashMeterCounterContext( _In_ const std::string &name, + _In_ const std::string &instance, _In_ sairedis::SaiInterface *vendor_sai, _In_ std::string dbCounters): - BaseCounterContext(name), m_dbCounters(dbCounters), m_vendorSai(vendor_sai) + BaseCounterContext(name, instance), m_dbCounters(dbCounters), m_vendorSai(vendor_sai) { SWSS_LOG_ENTER(); } @@ -1516,6 +1779,8 @@ void FlexCounter::addCounterPlugin( SWSS_LOG_ENTER(); m_isDiscarded = false; + uint32_t bulkChunkSize = 0; + std::string bulkChunkSizePerPrefix; for (auto& fvt: values) { @@ -1528,6 +1793,24 @@ void FlexCounter::addCounterPlugin( { setPollInterval(stoi(value)); } + else if (field == BULK_CHUNK_SIZE_FIELD) + { + bulkChunkSize = stoi(value); + for (auto &context : m_counterContext) + { + context.second->setBulkChunkSize(bulkChunkSize); + SWSS_LOG_NOTICE("Set counter context %s %s bulk size %u", m_instanceId.c_str(), COUNTER_TYPE_PORT.c_str(), bulkChunkSize); + } + } + else if (field == BULK_CHUNK_SIZE_PER_PREFIX_FIELD) + { + bulkChunkSizePerPrefix = value; + for (auto &context : m_counterContext) + { + context.second->setBulkChunkSizePerPrefix(bulkChunkSizePerPrefix); + SWSS_LOG_NOTICE("Set counter context %s %s bulk chunk prefix map %s", m_instanceId.c_str(), COUNTER_TYPE_PORT.c_str(), bulkChunkSizePerPrefix.c_str()); + } + } else if (field == FLEX_COUNTER_STATUS_FIELD) { setStatus(value); @@ -1550,6 +1833,18 @@ void FlexCounter::addCounterPlugin( SWSS_LOG_NOTICE("Do not double check bulk capability counter context %s %s", m_instanceId.c_str(), counterTypeRef->second.c_str()); } + + if (bulkChunkSize > 0) + { + getCounterContext(counterTypeRef->second)->setBulkChunkSize(bulkChunkSize); + SWSS_LOG_NOTICE("Create counter context %s %s with bulk size %u", m_instanceId.c_str(), counterTypeRef->second.c_str(), bulkChunkSize); + } + + if (!bulkChunkSizePerPrefix.empty()) + { + getCounterContext(counterTypeRef->second)->setBulkChunkSizePerPrefix(bulkChunkSizePerPrefix); + SWSS_LOG_NOTICE("Create counter context %s %s with bulk prefix map %s", m_instanceId.c_str(), counterTypeRef->second.c_str(), bulkChunkSizePerPrefix.c_str()); + } } else { @@ -1609,18 +1904,19 @@ bool FlexCounter::allPluginsEmpty() const } std::shared_ptr FlexCounter::createCounterContext( - _In_ const std::string& context_name) + _In_ const std::string& context_name, + _In_ const std::string& instance) { SWSS_LOG_ENTER(); if (context_name == COUNTER_TYPE_PORT) { - auto context = std::make_shared>(context_name, SAI_OBJECT_TYPE_PORT, m_vendorSai.get(), m_statsMode); + auto context = std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_PORT, m_vendorSai.get(), m_statsMode); context->always_check_supported_counters = true; return context; } else if (context_name == COUNTER_TYPE_PORT_DEBUG) { - auto context = std::make_shared>(context_name, SAI_OBJECT_TYPE_PORT, m_vendorSai.get(), m_statsMode); + auto context = std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_PORT, m_vendorSai.get(), m_statsMode); context->always_check_supported_counters = true; context->use_sai_stats_capa_query = false; context->use_sai_stats_ext = true; @@ -1629,25 +1925,25 @@ std::shared_ptr FlexCounter::createCounterContext( } else if (context_name == COUNTER_TYPE_QUEUE) { - auto context = std::make_shared>(context_name, SAI_OBJECT_TYPE_QUEUE, m_vendorSai.get(), m_statsMode); + auto context = std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_QUEUE, m_vendorSai.get(), m_statsMode); context->always_check_supported_counters = true; context->double_confirm_supported_counters = true; return context; } else if (context_name == COUNTER_TYPE_PG) { - auto context = std::make_shared>(context_name, SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP, m_vendorSai.get(), m_statsMode); + auto context = std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP, m_vendorSai.get(), m_statsMode); context->always_check_supported_counters = true; context->double_confirm_supported_counters = true; return context; } else if (context_name == COUNTER_TYPE_RIF) { - return std::make_shared>(context_name, SAI_OBJECT_TYPE_ROUTER_INTERFACE, m_vendorSai.get(), m_statsMode); + return std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_ROUTER_INTERFACE, m_vendorSai.get(), m_statsMode); } else if (context_name == COUNTER_TYPE_SWITCH_DEBUG) { - auto context = std::make_shared>(context_name, SAI_OBJECT_TYPE_SWITCH, m_vendorSai.get(), m_statsMode); + auto context = std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_SWITCH, m_vendorSai.get(), m_statsMode); context->always_check_supported_counters = true; context->use_sai_stats_capa_query = false; context->use_sai_stats_ext = true; @@ -1656,19 +1952,19 @@ std::shared_ptr FlexCounter::createCounterContext( } else if (context_name == COUNTER_TYPE_MACSEC_FLOW) { - auto context = std::make_shared>(context_name, SAI_OBJECT_TYPE_MACSEC_FLOW, m_vendorSai.get(), m_statsMode); + auto context = std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_MACSEC_FLOW, m_vendorSai.get(), m_statsMode); context->use_sai_stats_capa_query = false; return context; } else if (context_name == COUNTER_TYPE_MACSEC_SA) { - auto context = std::make_shared>(context_name, SAI_OBJECT_TYPE_MACSEC_SA, m_vendorSai.get(), m_statsMode); + auto context = std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_MACSEC_SA, m_vendorSai.get(), m_statsMode); context->use_sai_stats_capa_query = false; return context; } else if (context_name == COUNTER_TYPE_FLOW) { - auto context = std::make_shared>(context_name, SAI_OBJECT_TYPE_COUNTER, m_vendorSai.get(), m_statsMode); + auto context = std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_COUNTER, m_vendorSai.get(), m_statsMode); context->use_sai_stats_capa_query = false; context->use_sai_stats_ext = true; @@ -1676,41 +1972,41 @@ std::shared_ptr FlexCounter::createCounterContext( } else if (context_name == COUNTER_TYPE_TUNNEL) { - auto context = std::make_shared>(context_name, SAI_OBJECT_TYPE_TUNNEL, m_vendorSai.get(), m_statsMode); + auto context = std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_TUNNEL, m_vendorSai.get(), m_statsMode); context->use_sai_stats_capa_query = false; return context; } else if (context_name == COUNTER_TYPE_BUFFER_POOL) { - auto context = std::make_shared>(context_name, SAI_OBJECT_TYPE_BUFFER_POOL, m_vendorSai.get(), m_statsMode); + auto context = std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_BUFFER_POOL, m_vendorSai.get(), m_statsMode); context->always_check_supported_counters = true; return context; } else if (context_name == COUNTER_TYPE_ENI) { - auto context = std::make_shared>(context_name, (sai_object_type_t)SAI_OBJECT_TYPE_ENI, m_vendorSai.get(), m_statsMode); + auto context = std::make_shared>(context_name, instance, (sai_object_type_t)SAI_OBJECT_TYPE_ENI, m_vendorSai.get(), m_statsMode); context->always_check_supported_counters = true; return context; } else if (context_name == COUNTER_TYPE_METER_BUCKET) { - return std::make_shared(context_name, m_vendorSai.get(), m_dbCounters); + return std::make_shared(context_name, instance, m_vendorSai.get(), m_dbCounters); } else if (context_name == ATTR_TYPE_QUEUE) { - return std::make_shared>(context_name, SAI_OBJECT_TYPE_QUEUE, m_vendorSai.get(), m_statsMode); + return std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_QUEUE, m_vendorSai.get(), m_statsMode); } else if (context_name == ATTR_TYPE_PG) { - return std::make_shared>(context_name, SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP, m_vendorSai.get(), m_statsMode); + return std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP, m_vendorSai.get(), m_statsMode); } else if (context_name == ATTR_TYPE_MACSEC_SA) { - return std::make_shared>(context_name, SAI_OBJECT_TYPE_MACSEC_SA, m_vendorSai.get(), m_statsMode); + return std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_MACSEC_SA, m_vendorSai.get(), m_statsMode); } else if (context_name == ATTR_TYPE_ACL_COUNTER) { - return std::make_shared>(context_name, SAI_OBJECT_TYPE_ACL_COUNTER, m_vendorSai.get(), m_statsMode); + return std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_ACL_COUNTER, m_vendorSai.get(), m_statsMode); } SWSS_LOG_THROW("Invalid counter type %s", context_name.c_str()); @@ -1729,7 +2025,7 @@ std::shared_ptr FlexCounter::getCounterContext( return iter->second; } - auto ret = m_counterContext.emplace(name, createCounterContext(name)); + auto ret = m_counterContext.emplace(name, createCounterContext(name, m_instanceId)); return ret.first->second; } diff --git a/syncd/FlexCounter.h b/syncd/FlexCounter.h index 18d3f3af1..211dc7580 100644 --- a/syncd/FlexCounter.h +++ b/syncd/FlexCounter.h @@ -20,13 +20,19 @@ namespace syncd class BaseCounterContext { public: - BaseCounterContext(const std::string &name); + BaseCounterContext(const std::string &name, const std::string &instance); void addPlugins( _In_ const std::vector& shaStrings); void setNoDoubleCheckBulkCapability( _In_ bool); + virtual void setBulkChunkSize( + _In_ uint32_t bulkChunkSize); + + virtual void setBulkChunkSizePerPrefix( + _In_ const std::string& bulkChunkSizePerPrefix); + bool hasPlugin() const {return !m_plugins.empty();} void removePlugins() {m_plugins.clear();} @@ -51,7 +57,9 @@ namespace syncd protected: std::string m_name; + std::string m_instanceId; std::set m_plugins; + std::string m_bulkChunkSizePerPrefix; public: bool always_check_supported_counters = false; @@ -59,6 +67,7 @@ namespace syncd bool use_sai_stats_ext = false; bool double_confirm_supported_counters = false; bool no_double_check_bulk_capability = false; + uint32_t default_bulk_chunk_size; }; class FlexCounter { @@ -118,7 +127,8 @@ namespace syncd _In_ const std::string &name); std::shared_ptr createCounterContext( - _In_ const std::string &name); + _In_ const std::string &name, + _In_ const std::string &instance); void removeCounterContext( _In_ const std::string &name); diff --git a/unittest/syncd/TestFlexCounter.cpp b/unittest/syncd/TestFlexCounter.cpp index e5d374e17..96cb9125e 100644 --- a/unittest/syncd/TestFlexCounter.cpp +++ b/unittest/syncd/TestFlexCounter.cpp @@ -85,7 +85,11 @@ void testAddRemoveCounter( const std::vector& expectedValues, VerifyStatsFunc verifyFunc, bool autoRemoveDbEntry, - const std::string statsMode = STATS_MODE_READ) + const std::string statsMode = STATS_MODE_READ, + const std::string bulkChunkSize = "", + const std::string bulkChunkSizePerCounter = "", + bool bulkChunkSizeAfterPort = true, + const std::string pluginName = "") { SWSS_LOG_ENTER(); @@ -100,6 +104,20 @@ void testAddRemoveCounter( values.emplace_back(POLL_INTERVAL_FIELD, "1000"); values.emplace_back(FLEX_COUNTER_STATUS_FIELD, "enable"); values.emplace_back(STATS_MODE_FIELD, statsMode); + std::vector fcValues = values; + auto &bulkChunkSizeValues = bulkChunkSizeAfterPort ? fcValues : values; + if (!bulkChunkSize.empty()) + { + bulkChunkSizeValues.emplace_back(BULK_CHUNK_SIZE_FIELD, bulkChunkSize); + } + if (!bulkChunkSizePerCounter.empty()) + { + bulkChunkSizeValues.emplace_back(BULK_CHUNK_SIZE_PER_PREFIX_FIELD, bulkChunkSizePerCounter); + } + if (!pluginName.empty()) + { + values.emplace_back(pluginName, ""); + } fc.addCounterPlugin(values); values.clear(); @@ -109,6 +127,11 @@ void testAddRemoveCounter( fc.addCounter(object_id, object_id, values); } + if (bulkChunkSizeAfterPort) + { + fc.addCounterPlugin(bulkChunkSizeValues); + } + EXPECT_EQ(fc.isEmpty(), false); usleep(1000*1050); @@ -749,6 +772,178 @@ TEST(FlexCounter, bulkCounter) EXPECT_EQ(false, clearCalled); } +TEST(FlexCounter, bulkChunksize) +{ + /* + * Test logic + * 1. Generate counter values and store them whenever the bulk get stat is called after initialization + * 2. Convert stored counter values to string when the verify function is called + * and verify whether the database content aligns with the stored values + * 3. Verify whether values of all counter IDs of all objects have been generated + * 4. Verify whether the bulk chunk size is correct + */ + sai->mock_getStatsExt = [&](sai_object_type_t, sai_object_id_t, uint32_t number_of_counters, const sai_stat_id_t *, sai_stats_mode_t, uint64_t *counters) { + return SAI_STATUS_SUCCESS; + }; + sai->mock_getStats = [&](sai_object_type_t, sai_object_id_t, uint32_t number_of_counters, const sai_stat_id_t *, uint64_t *counters) { + return SAI_STATUS_SUCCESS; + }; + sai->mock_queryStatsCapability = [&](sai_object_id_t switch_id, sai_object_type_t object_type, sai_stat_capability_list_t *stats_capability) { + // For now, just return failure to make test simple, will write a singe test to cover querySupportedCounters + return SAI_STATUS_FAILURE; + }; + + // Map of number from {oid: {counter_id: counter value}} + std::map> counterValuesMap; + // Map of string from {oid: {counter_id: counter value}} + std::map> expectedValuesMap; + + std::set allCounterIds = { + "SAI_PORT_STAT_IF_IN_OCTETS", + "SAI_PORT_STAT_IF_IN_UCAST_PKTS", + "SAI_PORT_STAT_IF_OUT_QLEN", + "SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES", + "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES" + }; + std::set allObjectIds; + auto generateExpectedValues = [&]() + { + std::set allCounterValueSet; + for (const auto &oidRef : counterValuesMap) + { + auto &expected = expectedValuesMap[toOid(oidRef.first)]; + std::set localAllCounterIds = allCounterIds; + for (const auto &counters : oidRef.second) + { + // No duplicate counter value + EXPECT_EQ(allCounterValueSet.find(counters.second), allCounterValueSet.end()); + allCounterValueSet.insert(counters.second); + + // For each object, no unexpected counter ID + const auto &counterId = sai_serialize_port_stat((sai_port_stat_t)counters.first); + EXPECT_TRUE(localAllCounterIds.find(counterId) != localAllCounterIds.end()); + localAllCounterIds.erase(counterId); + + expected[counterId] = to_string(counters.second); + } + + // For each object, all expected counters are generated + EXPECT_TRUE(localAllCounterIds.empty()); + } + }; + + std::vector> counterRecord; + std::vector> valueRecord; + sai_uint64_t counterSeed = 0; + sai->mock_bulkGetStats = [&](sai_object_id_t, + sai_object_type_t, + uint32_t object_count, + const sai_object_key_t *object_keys, + uint32_t number_of_counters, + const sai_stat_id_t *counter_ids, + sai_stats_mode_t mode, + sai_status_t *object_status, + uint64_t *counters) + { + EXPECT_TRUE(mode == SAI_STATS_MODE_BULK_READ); + std::vector record; + std::vector value; + if (number_of_counters >= 5 && object_count == 1) + { + allObjectIds.insert(toOid(object_keys[0].key.object_id)); + // This call is to check whether bulk counter polling is supported during initialization + return SAI_STATUS_SUCCESS; + } + for (uint32_t i = 0; i < object_count; i++) + { + object_status[i] = SAI_STATUS_SUCCESS; + auto &counterMap = counterValuesMap[object_keys[i].key.object_id]; + for (uint32_t j = 0; j < number_of_counters; j++) + { + const auto &searchRef = counterMap.find(counter_ids[j]); + if (searchRef == counterMap.end()) + { + counterMap[counter_ids[j]] = ++counterSeed; + } + counters[i * number_of_counters + j] = counterMap[counter_ids[j]]; + record.emplace_back(counter_ids[j]); + value.emplace_back(counterSeed); + switch (counter_ids[j]) + { + case SAI_PORT_STAT_IF_IN_OCTETS: + case SAI_PORT_STAT_IF_IN_UCAST_PKTS: + // default chunk size 2, object number 6, object count 6 / 2 = 3 + EXPECT_EQ(object_count, 3); + break; + case SAI_PORT_STAT_IF_OUT_QLEN: + // queue length chunk size 0, object number 6, object count 6 + EXPECT_EQ(object_count, 6); + break; + case SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES: + case SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES: + // FEC chunk size 2, object number 6, object count 6 / 3 = 2 + EXPECT_EQ(object_count, 2); + default: + break; + } + } + } + return SAI_STATUS_SUCCESS; + }; + + auto counterVerifyFunc = [&] (swss::Table &countersTable, const std::string& key, const std::vector& counterIdNames, const std::vector&) + { + std::string value; + if (expectedValuesMap.empty()) + { + generateExpectedValues(); + } + auto const &searchRef = expectedValuesMap.find(key); + ASSERT_TRUE(searchRef != expectedValuesMap.end()); + auto &oidCounters = searchRef->second; + + for (auto const &counter : counterIdNames) + { + countersTable.hget(key, counter, value); + EXPECT_EQ(value, oidCounters[counter]); + oidCounters.erase(counter); + } + + EXPECT_TRUE(oidCounters.empty()); + expectedValuesMap.erase(searchRef); + + allObjectIds.erase(key); + }; + + testAddRemoveCounter( + 6, + SAI_OBJECT_TYPE_PORT, + PORT_COUNTER_ID_LIST, + {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS", "SAI_PORT_STAT_IF_OUT_QLEN", "SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES", "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES"}, + {}, + counterVerifyFunc, + false, + STATS_MODE_READ, + "3", + "SAI_PORT_STAT_IF_OUT_QLEN:0;SAI_PORT_STAT_IF_IN_FEC:2"); + EXPECT_TRUE(allObjectIds.empty()); + + testAddRemoveCounter( + 6, + SAI_OBJECT_TYPE_PORT, + PORT_COUNTER_ID_LIST, + {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS", "SAI_PORT_STAT_IF_OUT_QLEN", "SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES", "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES"}, + {}, + counterVerifyFunc, + false, + STATS_MODE_READ, + "3", + "SAI_PORT_STAT_IF_OUT_QLEN:0;SAI_PORT_STAT_IF_IN_FEC:2", + false, + PORT_PLUGIN_FIELD); + EXPECT_TRUE(allObjectIds.empty()); +} + TEST(FlexCounter, counterIdChange) { sai->mock_getStats = [&](sai_object_type_t, sai_object_id_t, uint32_t number_of_counters, const sai_stat_id_t *, uint64_t *counters) {