From 5ee56104959edc2752fcc8bcef0fb5bb30dee826 Mon Sep 17 00:00:00 2001
From: mssonicbld <79238446+mssonicbld@users.noreply.github.com>
Date: Thu, 27 Feb 2025 13:02:02 +0800
Subject: [PATCH] [code sync] Merge code from sonic-net/sonic-sairedis:202411
to 202412 (#24)
```
* f83634d9 - (HEAD -> 202412) Merge branch '202411' of https://github.com/sonic-net/sonic-sairedis into 202412 (2025-02-27) [Sonic Automation]
* 8c17d4b6 - (origin/202411) Revert "Do not enter vendor SAI critical section for counter polling/clearing operations (#1450)" (#1541) (2025-02-27) [mssonicbld]
* 3df03e19 - Optimize counter polling interval by making it more accurate (#1457) (#1534) (2025-02-26) [Stephen Sun]
* d884ff9a - [syncd] Move logSet logGet under mutex to prevent race condition (#1520) (#1538) (2025-02-20) [Kamil Cudnik]
* ec8b3c30 - Fix pipeline errors related to rsyslogd and libswsscommon installation (#1535) (2025-02-19) [mssonicbld]
* 6b263b8e - [FC] Support Policer Counter (#1533) (2025-02-19) [mssonicbld]
* e53489e5 - [syncd] Update log level for bulk api (#1532) (2025-02-18) [Jianyue Wu]
* 7ae00e53 - Define bulk chunk size and bulk chunk size per counter ID (#1528) (2025-02-11) [mssonicbld]
* f35e743a - [nvidia] Skip SAI discovery on ports (#1524) (2025-02-07) [mssonicbld]
* bf049ed2 - Use sonictest pool instead of sonic-common and fix arm64 issue. (#1516) (2025-02-05) [mssonicbld]
* ffe371d6 - [syncd] Support bulk set in INIT_VIEW mode (#1517) (2025-02-05) [mssonicbld]
```
---
lib/RedisRemoteSaiInterface.cpp | 2 +
syncd/FlexCounter.cpp | 468 ++++++++++++++++++++++++++---
syncd/FlexCounter.h | 14 +-
syncd/VendorSai.cpp | 5 +
tests/aspell.en.pws | 1 +
unittest/syncd/TestFlexCounter.cpp | 231 +++++++++++++-
unittest/syncd/TestVendorSai.cpp | 22 ++
7 files changed, 694 insertions(+), 49 deletions(-)
diff --git a/lib/RedisRemoteSaiInterface.cpp b/lib/RedisRemoteSaiInterface.cpp
index 4c024710..08ffb193 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/syncd/FlexCounter.cpp b/syncd/FlexCounter.cpp
index bbf1f2d9..ca42fbaa 100644
--- a/syncd/FlexCounter.cpp
+++ b/syncd/FlexCounter.cpp
@@ -42,8 +42,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();
}
@@ -74,6 +75,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
@@ -144,6 +159,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
@@ -435,16 +452,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();
}
@@ -542,12 +562,260 @@ 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;
+ createCounterBulkChunkSizePerPrefixPartition(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());
+ }
+ }
+
+ bool parseBulkChunkSizePerPrefixConfigString(
+ _In_ const std::string& prefixConfigString)
+ {
+ SWSS_LOG_ENTER();
+
+ m_counterChunkSizeMapFromPrefix.clear();
+
+ if (!prefixConfigString.empty() && prefixConfigString != "NULL")
+ {
+ try
+ {
+ 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]);
+ }
+ }
+ catch (...)
+ {
+ SWSS_LOG_ERROR("Invalid bulk chunk size per counter ID field %s", prefixConfigString.c_str());
+ m_counterChunkSizeMapFromPrefix.clear();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void createCounterBulkChunkSizePerPrefixPartition(
+ _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 updated 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;
+
+ // No operation if the input string is invalid or no bulk context has been created
+ if (!parseBulkChunkSizePerPrefixConfigString(bulkChunkSizePerPrefix) || m_bulkContexts.empty())
+ {
+ return;
+ }
+
+ if (m_bulkContexts.size() == 1)
+ {
+ // Only one bulk context exists which means
+ // it is the first time per counter chunk size is configured and a unified counter ID set is polled for all objects
+ auto it = m_bulkContexts.begin();
+ std::shared_ptr singleBulkContext = it->second;
+ const std::vector &allCounterIds = singleBulkContext.get()->counter_ids;
+ std::map> counterChunkSizePerPrefix;
+ std::vector defaultPartition;
+
+ if (m_counterChunkSizeMapFromPrefix.empty())
+ {
+ // There is still no per counter prefix chunk size configured as the chunk size map is still empty.
+ singleBulkContext.get()->default_bulk_chunk_size = default_bulk_chunk_size;
+ }
+ else
+ {
+ // Split the counter IDs according to the counter ID prefix mapping and store them into m_bulkContexts
+ SWSS_LOG_NOTICE("Split counter IDs set by prefix for the first time %s", bulkChunkSizePerPrefix.c_str());
+ createCounterBulkChunkSizePerPrefixPartition(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.get()->object_statuses.size());
+ bulkContext.get()->object_vids = singleBulkContext.get()->object_vids;
+ bulkContext.get()->object_keys = singleBulkContext.get()->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());
+ setBulkStatsContext(defaultPartition, singleBulkContext);
+ singleBulkContext.get()->counters.resize(singleBulkContext.get()->counter_ids.size() * singleBulkContext.get()->object_vids.size());
+ m_bulkContexts.erase(it);
+ SWSS_LOG_INFO("Removed the previous default counter partition");
+ }
+ }
+ else if (m_counterChunkSizeMapFromPrefix.empty())
+ {
+ // There have been multiple bulk contexts which can result from
+ // 1. per counter prefix chunk size configuration
+ // 2. different objects support different counter ID set
+ // And there is no per counter prefix chunk size configured any more
+ // Multiple bulk contexts will be merged into one if they share the same object IDs set, which means case (1).
+ std::set oid_set;
+ std::vector counter_ids;
+ std::shared_ptr defaultBulkContext;
+ for (auto &context : m_bulkContexts)
+ {
+ if (oid_set.empty())
+ {
+ oid_set.insert(context.second.get()->object_vids.begin(), context.second.get()->object_vids.end());
+ }
+ else
+ {
+ std::set tmp_oid_set(context.second.get()->object_vids.begin(), context.second.get()->object_vids.end());
+ if (tmp_oid_set != oid_set)
+ {
+ SWSS_LOG_ERROR("Can not merge partition because they contains different objects");
+ return;
+ }
+ }
+ if (context.second.get()->name == "default")
+ {
+ defaultBulkContext = context.second;
+ }
+ counter_ids.insert(counter_ids.end(), context.second.get()->counter_ids.begin(), context.second.get()->counter_ids.end());
+ }
+
+ m_bulkContexts.clear();
+
+ std::sort(counter_ids.begin(), counter_ids.end());
+ setBulkStatsContext(counter_ids, defaultBulkContext);
+ defaultBulkContext.get()->counters.resize(defaultBulkContext.get()->counter_ids.size() * defaultBulkContext.get()->object_vids.size());
+ }
+ else
+ {
+ // Multiple bulk contexts and per counter prefix chunk size
+ // Update the chunk size only in this case.
+ 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 &it : m_bulkContexts)
+ {
+ auto &context = *it.second.get();
+ 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.name.c_str(),
+ context.object_keys.size(),
+ context.counter_ids.size(),
+ context.counters.size());
+ }
}
void removeObject(
@@ -628,6 +896,9 @@ class CounterContext : public BaseCounterContext
{
return;
}
+
+ SWSS_LOG_DEBUG("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(),
@@ -646,6 +917,8 @@ class CounterContext : public BaseCounterContext
std::for_each(m_plugins.begin(),
m_plugins.end(),
[&] (auto &sha) { runRedisScript(counters_db, sha, idStrings, argv); });
+
+ SWSS_LOG_DEBUG("After running plugin %s %s", m_instanceId.c_str(), m_name.c_str());
}
bool hasObject() const override
@@ -748,20 +1021,45 @@ 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_DEBUG("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;
+ }
+ }
+
+ SWSS_LOG_INFO("After getting bulk %s %s %s total %u objects", m_instanceId.c_str(), m_name.c_str(), ctx.name.c_str(), size);
+
+ 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++)
@@ -777,13 +1075,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_DEBUG("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);
@@ -792,10 +1095,23 @@ 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;
+ ret.first->second.get()->counter_ids = counterIds;
return ret.first->second;
}
+ void setBulkStatsContext(
+ _In_ const std::vector& counterIds,
+ _In_ const std::shared_ptr ptr)
+ {
+ SWSS_LOG_ENTER();
+ m_bulkContexts.emplace(counterIds, ptr);
+ ptr.get()->counter_ids = counterIds;
+ }
+
void addBulkStatsContext(
_In_ sai_object_id_t vid,
_In_ sai_object_id_t rid,
@@ -807,10 +1123,6 @@ class CounterContext : public BaseCounterContext
sai_object_key_t object_key;
object_key.key.object_id = rid;
ctx.object_keys.push_back(object_key);
- if (ctx.counter_ids.empty())
- {
- ctx.counter_ids = counterIds;
- }
ctx.object_statuses.push_back(SAI_STATUS_SUCCESS);
ctx.counters.resize(counterIds.size() * ctx.object_keys.size());
}
@@ -819,6 +1131,7 @@ class CounterContext : public BaseCounterContext
_In_ sai_object_id_t vid)
{
SWSS_LOG_ENTER();
+ std::set> bulkContextsToBeRemoved;
bool found = false;
for (auto iter = m_bulkContexts.begin(); iter != m_bulkContexts.end(); iter++)
{
@@ -833,7 +1146,9 @@ class CounterContext : public BaseCounterContext
ctx.object_vids.erase(vid_iter);
if (ctx.object_vids.empty())
{
- m_bulkContexts.erase(iter);
+ // It can change the order of the map to erase an element in a loop iterating the map
+ // which can cause some elements to be skipped or iterated for multiple times
+ bulkContextsToBeRemoved.insert(iter->first);
}
else
{
@@ -843,7 +1158,20 @@ 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;
+ }
+ }
+
+ for (auto iter : bulkContextsToBeRemoved)
+ {
+ m_bulkContexts.erase(iter);
}
return found;
@@ -856,6 +1184,7 @@ class CounterContext : public BaseCounterContext
{
SWSS_LOG_ENTER();
BulkContextType ctx;
+ ctx.counter_ids = counter_ids;
addBulkStatsContext(vid, rid, counter_ids, ctx);
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(
@@ -986,10 +1315,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();
}
@@ -1073,9 +1403,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();
}
@@ -1534,6 +1865,8 @@ void FlexCounter::addCounterPlugin(
SWSS_LOG_ENTER();
m_isDiscarded = false;
+ uint32_t bulkChunkSize = 0;
+ std::string bulkChunkSizePerPrefix;
for (auto& fvt: values)
{
@@ -1546,6 +1879,34 @@ void FlexCounter::addCounterPlugin(
{
setPollInterval(stoi(value));
}
+ else if (field == BULK_CHUNK_SIZE_FIELD)
+ {
+ if (value != "NULL")
+ {
+ try
+ {
+ bulkChunkSize = stoi(value);
+ }
+ catch (...)
+ {
+ SWSS_LOG_ERROR("Invalid bulk chunk size %s", value.c_str());
+ }
+ }
+ for (auto &context : m_counterContext)
+ {
+ SWSS_LOG_NOTICE("Set counter context %s %s bulk size %u", m_instanceId.c_str(), COUNTER_TYPE_PORT.c_str(), bulkChunkSize);
+ context.second->setBulkChunkSize(bulkChunkSize);
+ }
+ }
+ else if (field == BULK_CHUNK_SIZE_PER_PREFIX_FIELD)
+ {
+ bulkChunkSizePerPrefix = value;
+ for (auto &context : m_counterContext)
+ {
+ 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());
+ context.second->setBulkChunkSizePerPrefix(bulkChunkSizePerPrefix);
+ }
+ }
else if (field == FLEX_COUNTER_STATUS_FIELD)
{
setStatus(value);
@@ -1568,6 +1929,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
{
@@ -1627,18 +2000,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;
@@ -1647,25 +2021,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;
@@ -1674,13 +2048,13 @@ 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->always_check_supported_counters = true;
context->use_sai_stats_capa_query = false;
context->dont_clear_support_counter = true;
@@ -1688,7 +2062,7 @@ std::shared_ptr FlexCounter::createCounterContext(
}
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;
@@ -1696,41 +2070,45 @@ 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);
+ }
+ else if (context_name == COUNTER_TYPE_POLICER)
+ {
+ return std::make_shared>(context_name, instance, SAI_OBJECT_TYPE_POLICER, m_vendorSai.get(), m_statsMode);
}
else if (context_name == COUNTER_TYPE_POLICER)
{
@@ -1753,7 +2131,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 757ae0af..9053046b 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;
@@ -60,6 +68,7 @@ namespace syncd
bool double_confirm_supported_counters = false;
bool no_double_check_bulk_capability = false;
bool dont_clear_support_counter = false;
+ uint32_t default_bulk_chunk_size;
};
class FlexCounter
{
@@ -119,7 +128,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/syncd/VendorSai.cpp b/syncd/VendorSai.cpp
index f2fe232b..fd9f0662 100644
--- a/syncd/VendorSai.cpp
+++ b/syncd/VendorSai.cpp
@@ -313,6 +313,7 @@ sai_status_t VendorSai::getStats(
_In_ const sai_stat_id_t *counter_ids,
_Out_ uint64_t *counters)
{
+ MUTEX();
SWSS_LOG_ENTER();
VENDOR_CHECK_API_INITIALIZED();
@@ -350,6 +351,7 @@ sai_status_t VendorSai::getStatsExt(
_In_ sai_stats_mode_t mode,
_Out_ uint64_t *counters)
{
+ MUTEX();
SWSS_LOG_ENTER();
VENDOR_CHECK_API_INITIALIZED();
@@ -364,6 +366,7 @@ sai_status_t VendorSai::clearStats(
_In_ uint32_t number_of_counters,
_In_ const sai_stat_id_t *counter_ids)
{
+ MUTEX();
SWSS_LOG_ENTER();
VENDOR_CHECK_API_INITIALIZED();
@@ -383,6 +386,7 @@ sai_status_t VendorSai::bulkGetStats(
_Inout_ sai_status_t *object_statuses,
_Out_ uint64_t *counters)
{
+ MUTEX();
SWSS_LOG_ENTER();
VENDOR_CHECK_API_INITIALIZED();
@@ -410,6 +414,7 @@ sai_status_t VendorSai::bulkClearStats(
_In_ sai_stats_mode_t mode,
_Inout_ sai_status_t *object_statuses)
{
+ MUTEX();
SWSS_LOG_ENTER();
VENDOR_CHECK_API_INITIALIZED();
diff --git a/tests/aspell.en.pws b/tests/aspell.en.pws
index 1908c583..e29a4970 100644
--- a/tests/aspell.en.pws
+++ b/tests/aspell.en.pws
@@ -34,6 +34,7 @@ ecmp
ECMP
FDB
FDBs
+FEC
FIXME
FlexCounter
gbsyncd
diff --git a/unittest/syncd/TestFlexCounter.cpp b/unittest/syncd/TestFlexCounter.cpp
index cdcc1d20..f1ad5bb6 100644
--- a/unittest/syncd/TestFlexCounter.cpp
+++ b/unittest/syncd/TestFlexCounter.cpp
@@ -85,7 +85,12 @@ 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 = "",
+ bool immediatelyRemoveBulkChunkSizePerCounter = false)
{
SWSS_LOG_ENTER();
@@ -100,6 +105,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 +128,17 @@ void testAddRemoveCounter(
fc.addCounter(object_id, object_id, values);
}
+ if (bulkChunkSizeAfterPort)
+ {
+ fc.addCounterPlugin(bulkChunkSizeValues);
+ if (immediatelyRemoveBulkChunkSizePerCounter)
+ {
+ bulkChunkSizeValues.clear();
+ bulkChunkSizeValues.emplace_back(BULK_CHUNK_SIZE_PER_PREFIX_FIELD, "");
+ fc.addCounterPlugin(bulkChunkSizeValues);
+ }
+ }
+
EXPECT_EQ(fc.isEmpty(), false);
usleep(1000*1050);
@@ -761,6 +791,204 @@ 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;
+ uint32_t unifiedBulkChunkSize = 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);
+ if (unifiedBulkChunkSize > 0)
+ {
+ if (object_count != unifiedBulkChunkSize)
+ {
+ EXPECT_EQ(object_count, unifiedBulkChunkSize);
+ }
+ continue;
+ }
+ 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());
+
+ unifiedBulkChunkSize = 3;
+ 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",
+ true,
+ "",
+ true);
+ 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) {
@@ -1152,4 +1380,3 @@ TEST(FlexCounter, noEniDashMeterCounter)
counterVerifyFunc,
false);
}
-
diff --git a/unittest/syncd/TestVendorSai.cpp b/unittest/syncd/TestVendorSai.cpp
index f237bc00..241ad723 100644
--- a/unittest/syncd/TestVendorSai.cpp
+++ b/unittest/syncd/TestVendorSai.cpp
@@ -156,6 +156,28 @@ TEST(VendorSai, bulkGetStats)
nullptr));
}
+TEST(VendorSai, getStatsExt)
+{
+ VendorSai sai;
+ sai.apiInitialize(0, &test_services);
+ ASSERT_EQ(SAI_STATUS_NOT_SUPPORTED, sai.getStatsExt(SAI_OBJECT_TYPE_NULL,
+ SAI_NULL_OBJECT_ID,
+ 0,
+ nullptr,
+ SAI_STATS_MODE_READ,
+ nullptr));
+}
+
+TEST(VendorSai, clearStats)
+{
+ VendorSai sai;
+ sai.apiInitialize(0, &test_services);
+ ASSERT_EQ(SAI_STATUS_NOT_SUPPORTED, sai.clearStats(SAI_OBJECT_TYPE_NULL,
+ SAI_NULL_OBJECT_ID,
+ 0,
+ nullptr));
+}
+
sai_object_id_t create_port(
_In_ VendorSai& sai,
_In_ sai_object_id_t switch_id)