syncd/FlexCounter.cpp (2,477 lines of code) (raw):
#include "FlexCounter.h"
#include "VidManager.h"
#include "meta/sai_serialize.h"
#include "swss/redisapi.h"
#include "swss/tokenize.h"
#include <inttypes.h>
#include <vector>
using namespace syncd;
using namespace std;
#define MUTEX std::unique_lock<std::mutex> _lock(m_mtx);
#define MUTEX_UNLOCK _lock.unlock();
static const std::string COUNTER_TYPE_PORT = "Port Counter";
static const std::string COUNTER_TYPE_PORT_DEBUG = "Port Debug Counter";
static const std::string COUNTER_TYPE_QUEUE = "Queue Counter";
static const std::string COUNTER_TYPE_PG = "Priority Group Counter";
static const std::string COUNTER_TYPE_RIF = "Rif Counter";
static const std::string COUNTER_TYPE_SWITCH_DEBUG = "Switch Debug Counter";
static const std::string COUNTER_TYPE_MACSEC_FLOW = "MACSEC Flow Counter";
static const std::string COUNTER_TYPE_MACSEC_SA = "MACSEC SA Counter";
static const std::string COUNTER_TYPE_FLOW = "Flow Counter";
static const std::string COUNTER_TYPE_TUNNEL = "Tunnel Counter";
static const std::string COUNTER_TYPE_BUFFER_POOL = "Buffer Pool Counter";
static const std::string COUNTER_TYPE_ENI = "DASH ENI Counter";
static const std::string COUNTER_TYPE_METER_BUCKET = "DASH Meter Bucket Counter";
static const std::string COUNTER_TYPE_POLICER = "Policer Counter";
static const std::string COUNTER_TYPE_SRV6 = "SRv6 Counter";
static const std::string ATTR_TYPE_QUEUE = "Queue Attribute";
static const std::string ATTR_TYPE_PG = "Priority Group Attribute";
static const std::string ATTR_TYPE_MACSEC_SA = "MACSEC SA Attribute";
static const std::string ATTR_TYPE_ACL_COUNTER = "ACL Counter Attribute";
static const std::string COUNTER_TYPE_WRED_ECN_QUEUE = "WRED Queue Counter";
static const std::string COUNTER_TYPE_WRED_ECN_PORT = "WRED Port Counter";
const std::map<std::string, std::string> FlexCounter::m_plugIn2CounterType = {
{QUEUE_PLUGIN_FIELD, COUNTER_TYPE_QUEUE},
{PG_PLUGIN_FIELD, COUNTER_TYPE_PG},
{PORT_PLUGIN_FIELD, COUNTER_TYPE_PORT},
{RIF_PLUGIN_FIELD, COUNTER_TYPE_RIF},
{BUFFER_POOL_PLUGIN_FIELD, COUNTER_TYPE_BUFFER_POOL},
{TUNNEL_PLUGIN_FIELD, COUNTER_TYPE_TUNNEL},
{FLOW_COUNTER_PLUGIN_FIELD, COUNTER_TYPE_FLOW},
{WRED_QUEUE_PLUGIN_FIELD, COUNTER_TYPE_WRED_ECN_QUEUE},
{WRED_PORT_PLUGIN_FIELD, COUNTER_TYPE_WRED_ECN_PORT}};
const std::map<std::tuple<sai_object_type_t, std::string>, std::string> FlexCounter::m_objectTypeField2CounterType = {
{{SAI_OBJECT_TYPE_PORT, PORT_COUNTER_ID_LIST}, COUNTER_TYPE_PORT},
{{SAI_OBJECT_TYPE_PORT, PORT_DEBUG_COUNTER_ID_LIST}, COUNTER_TYPE_PORT_DEBUG},
{{SAI_OBJECT_TYPE_QUEUE, QUEUE_COUNTER_ID_LIST}, COUNTER_TYPE_QUEUE},
{{SAI_OBJECT_TYPE_QUEUE, QUEUE_ATTR_ID_LIST}, ATTR_TYPE_QUEUE},
{{SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP, PG_COUNTER_ID_LIST}, COUNTER_TYPE_PG},
{{SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP, PG_ATTR_ID_LIST}, ATTR_TYPE_PG},
{{SAI_OBJECT_TYPE_ROUTER_INTERFACE, RIF_COUNTER_ID_LIST}, COUNTER_TYPE_RIF},
{{SAI_OBJECT_TYPE_SWITCH, SWITCH_DEBUG_COUNTER_ID_LIST}, COUNTER_TYPE_SWITCH_DEBUG},
{{SAI_OBJECT_TYPE_MACSEC_FLOW, MACSEC_FLOW_COUNTER_ID_LIST}, COUNTER_TYPE_MACSEC_FLOW},
{{SAI_OBJECT_TYPE_MACSEC_SA, MACSEC_SA_COUNTER_ID_LIST}, COUNTER_TYPE_MACSEC_SA},
{{SAI_OBJECT_TYPE_MACSEC_SA, MACSEC_SA_ATTR_ID_LIST}, ATTR_TYPE_MACSEC_SA},
{{SAI_OBJECT_TYPE_ACL_COUNTER, ACL_COUNTER_ATTR_ID_LIST}, ATTR_TYPE_ACL_COUNTER},
{{SAI_OBJECT_TYPE_COUNTER, FLOW_COUNTER_ID_LIST}, COUNTER_TYPE_FLOW},
{{SAI_OBJECT_TYPE_POLICER, POLICER_COUNTER_ID_LIST}, COUNTER_TYPE_POLICER},
{{SAI_OBJECT_TYPE_TUNNEL, TUNNEL_COUNTER_ID_LIST}, COUNTER_TYPE_TUNNEL},
{{(sai_object_type_t)SAI_OBJECT_TYPE_ENI, ENI_COUNTER_ID_LIST}, COUNTER_TYPE_ENI},
{{(sai_object_type_t)SAI_OBJECT_TYPE_ENI, DASH_METER_COUNTER_ID_LIST}, COUNTER_TYPE_METER_BUCKET},
{{SAI_OBJECT_TYPE_COUNTER, SRV6_COUNTER_ID_LIST}, COUNTER_TYPE_SRV6},
};
BaseCounterContext::BaseCounterContext(const std::string &name, const std::string &instance):
m_name(name),
m_instanceId(instance)
{
SWSS_LOG_ENTER();
}
void BaseCounterContext::addPlugins(
_In_ const std::vector<std::string>& shaStrings)
{
SWSS_LOG_ENTER();
for (const auto &sha : shaStrings)
{
auto ret = m_plugins.insert(sha);
if (ret.second)
{
SWSS_LOG_NOTICE("%s counters plugin %s registered", m_name.c_str(), sha.c_str());
}
else
{
SWSS_LOG_ERROR("Plugin %s already registered", sha.c_str());
}
}
}
void BaseCounterContext::setNoDoubleCheckBulkCapability(
_In_ bool noDoubleCheckBulkCapability)
{
SWSS_LOG_ENTER();
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 <typename StatType,
typename Enable = void>
struct CounterIds
{
CounterIds(
_In_ sai_object_id_t id,
_In_ const std::vector<StatType> &ids
): rid(id), counter_ids(ids) {}
void setStatsMode(sai_stats_mode_t statsMode) {}
sai_stats_mode_t getStatsMode() const
{
SWSS_LOG_ENTER();
SWSS_LOG_THROW("This counter type has no stats mode field");
// GCC 8.3 requires a return value here
return SAI_STATS_MODE_READ_AND_CLEAR;
}
sai_object_id_t rid;
std::vector<StatType> counter_ids;
};
// CounterIds structure contains stats mode, now buffer pool is the only one
// has member stats_mode.
template <typename StatType>
struct CounterIds<StatType, typename std::enable_if_t<std::is_same<StatType, sai_buffer_pool_stat_t>::value> >
{
CounterIds(
_In_ sai_object_id_t id,
_In_ const std::vector<StatType> &ids
): rid(id), counter_ids(ids)
{
SWSS_LOG_ENTER();
}
void setStatsMode(sai_stats_mode_t statsMode)
{
SWSS_LOG_ENTER();
stats_mode = statsMode;
}
sai_stats_mode_t getStatsMode() const
{
SWSS_LOG_ENTER();
return stats_mode;
}
sai_object_id_t rid;
std::vector<StatType> counter_ids;
sai_stats_mode_t stats_mode;
};
template <typename T>
struct HasStatsMode
{
template <typename U>
static void check(decltype(&U::stats_mode));
template <typename U>
static int check(...);
enum { value = std::is_void<decltype(check<T>(0))>::value };
};
// BulkStatsContext is used to store bulk counter related
// data and avoid construct them each time calling SAI bulk API
template <typename StatType>
struct BulkStatsContext
{
std::vector<sai_object_id_t> object_vids;
std::vector<sai_object_key_t> object_keys;
std::vector<StatType> counter_ids;
std::vector<sai_status_t> object_statuses;
std::vector<uint64_t> counters;
std::string name;
uint32_t default_bulk_chunk_size;
};
// TODO: use if const expression when cpp17 is supported
template <typename StatType>
std::string serializeStat(
_In_ const StatType stat)
{
SWSS_LOG_ENTER();
SWSS_LOG_THROW("serializeStat for default type parameter is not implemented");
// GCC 8.3 requires a return value here
return "";
}
template <>
std::string serializeStat(
_In_ const sai_port_stat_t stat)
{
SWSS_LOG_ENTER();
return sai_serialize_port_stat(stat);
}
template <>
std::string serializeStat(
_In_ const sai_policer_stat_t stat)
{
SWSS_LOG_ENTER();
return sai_serialize_policer_stat(stat);
}
template <>
std::string serializeStat(
_In_ const sai_queue_stat_t stat)
{
SWSS_LOG_ENTER();
return sai_serialize_queue_stat(stat);
}
template <>
std::string serializeStat(
_In_ const sai_ingress_priority_group_stat_t stat)
{
SWSS_LOG_ENTER();
return sai_serialize_ingress_priority_group_stat(stat);
}
template <>
std::string serializeStat(
_In_ const sai_router_interface_stat_t stat)
{
SWSS_LOG_ENTER();
return sai_serialize_router_interface_stat(stat);
}
template <>
std::string serializeStat(
_In_ const sai_switch_stat_t stat)
{
SWSS_LOG_ENTER();
return sai_serialize_switch_stat(stat);
}
template <>
std::string serializeStat(
_In_ const sai_macsec_flow_stat_t stat)
{
SWSS_LOG_ENTER();
return sai_serialize_macsec_flow_stat(stat);
}
template <>
std::string serializeStat(
_In_ const sai_macsec_sa_stat_t stat)
{
SWSS_LOG_ENTER();
return sai_serialize_macsec_sa_stat(stat);
}
template <>
std::string serializeStat(
_In_ const sai_counter_stat_t stat)
{
SWSS_LOG_ENTER();
return sai_serialize_counter_stat(stat);
}
template <>
std::string serializeStat(
_In_ const sai_tunnel_stat_t stat)
{
SWSS_LOG_ENTER();
return sai_serialize_tunnel_stat(stat);
}
template <>
std::string serializeStat(
_In_ const sai_buffer_pool_stat_t stat)
{
SWSS_LOG_ENTER();
return sai_serialize_buffer_pool_stat(stat);
}
template <>
std::string serializeStat(
_In_ const sai_eni_stat_t stat)
{
SWSS_LOG_ENTER();
return sai_serialize_eni_stat(stat);
}
template <>
std::string serializeStat(
_In_ const sai_meter_bucket_entry_stat_t stat)
{
SWSS_LOG_ENTER();
return sai_serialize_meter_bucket_entry_stat(stat);
}
template <typename StatType>
void deserializeStat(
_In_ const char* name,
_Out_ StatType *stat)
{
SWSS_LOG_ENTER();
SWSS_LOG_THROW("deserializeStat for default type parameter is not implemented");
}
template <>
void deserializeStat(
_In_ const char* name,
_Out_ sai_port_stat_t *stat)
{
SWSS_LOG_ENTER();
sai_deserialize_port_stat(name, stat);
}
template <>
void deserializeStat(
_In_ const char* name,
_Out_ sai_policer_stat_t *stat)
{
SWSS_LOG_ENTER();
sai_deserialize_policer_stat(name, stat);
}
template <>
void deserializeStat(
_In_ const char* name,
_Out_ sai_queue_stat_t *stat)
{
SWSS_LOG_ENTER();
sai_deserialize_queue_stat(name, stat);
}
template <>
void deserializeStat(
_In_ const char* name,
_Out_ sai_ingress_priority_group_stat_t *stat)
{
SWSS_LOG_ENTER();
sai_deserialize_ingress_priority_group_stat(name, stat);
}
template <>
void deserializeStat(
_In_ const char* name,
_Out_ sai_router_interface_stat_t *stat)
{
SWSS_LOG_ENTER();
sai_deserialize_router_interface_stat(name, stat);
}
template <>
void deserializeStat(
_In_ const char* name,
_Out_ sai_switch_stat_t *stat)
{
SWSS_LOG_ENTER();
sai_deserialize_switch_stat(name, stat);
}
template <>
void deserializeStat(
_In_ const char* name,
_Out_ sai_macsec_flow_stat_t *stat)
{
SWSS_LOG_ENTER();
sai_deserialize_macsec_flow_stat(name, stat);
}
template <>
void deserializeStat(
_In_ const char* name,
_Out_ sai_macsec_sa_stat_t *stat)
{
SWSS_LOG_ENTER();
sai_deserialize_macsec_sa_stat(name, stat);
}
template <>
void deserializeStat(
_In_ const char* name,
_Out_ sai_counter_stat_t *stat)
{
SWSS_LOG_ENTER();
sai_deserialize_counter_stat(name, stat);
}
template <>
void deserializeStat(
_In_ const char* name,
_Out_ sai_tunnel_stat_t *stat)
{
SWSS_LOG_ENTER();
sai_deserialize_tunnel_stat(name, stat);
}
template <>
void deserializeStat(
_In_ const char* name,
_Out_ sai_buffer_pool_stat_t *stat)
{
SWSS_LOG_ENTER();
sai_deserialize_buffer_pool_stat(name, stat);
}
template <>
void deserializeStat(
_In_ const char* name,
_Out_ sai_eni_stat_t *stat)
{
SWSS_LOG_ENTER();
sai_deserialize_eni_stat(name, stat);
}
template <>
void deserializeStat(
_In_ const char* name,
_Out_ sai_meter_bucket_entry_stat_t *stat)
{
SWSS_LOG_ENTER();
sai_deserialize_meter_bucket_entry_stat(name, stat);
}
template <typename AttrType>
void deserializeAttr(
_In_ const std::string& name,
_Out_ AttrType &attr)
{
SWSS_LOG_ENTER();
SWSS_LOG_THROW("deserializeAttr for default type parameter is not implemented");
}
template <>
void deserializeAttr(
_In_ const std::string& name,
_Out_ sai_queue_attr_t &attr)
{
SWSS_LOG_ENTER();
sai_deserialize_queue_attr(name, attr);
}
template <>
void deserializeAttr(
_In_ const std::string& name,
_Out_ sai_ingress_priority_group_attr_t &attr)
{
SWSS_LOG_ENTER();
sai_deserialize_ingress_priority_group_attr(name, attr);
}
template <>
void deserializeAttr(
_In_ const std::string& name,
_Out_ sai_macsec_sa_attr_t &attr)
{
SWSS_LOG_ENTER();
sai_deserialize_macsec_sa_attr(name, attr);
}
template <>
void deserializeAttr(
_In_ const std::string& name,
_Out_ sai_acl_counter_attr_t &attr)
{
SWSS_LOG_ENTER();
sai_deserialize_acl_counter_attr(name, attr);
}
template <typename StatType>
class CounterContext : public BaseCounterContext
{
std::map<std::string, uint32_t> m_counterChunkSizeMapFromPrefix;
protected:
sai_object_id_t m_switchId = SAI_NULL_OBJECT_ID;
public:
typedef CounterIds<StatType> CounterIdsType;
typedef BulkStatsContext<StatType> 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, instance), m_objectType(object_type), m_vendorSai(vendor_sai), m_groupStatsMode(stats_mode)
{
SWSS_LOG_ENTER();
}
// For those object type who support per object stats mode, e.g. buffer pool.
virtual void addObject(
_In_ sai_object_id_t vid,
_In_ sai_object_id_t rid,
_In_ const std::vector<std::string> &idStrings,
_In_ const std::string &per_object_stats_mode) override
{
SWSS_LOG_ENTER();
sai_stats_mode_t instance_stats_mode = SAI_STATS_MODE_READ_AND_CLEAR;
sai_stats_mode_t effective_stats_mode;
// TODO: use if const expression when c++17 is supported
if (HasStatsMode<CounterIdsType>::value)
{
if (per_object_stats_mode == STATS_MODE_READ_AND_CLEAR)
{
instance_stats_mode = SAI_STATS_MODE_READ_AND_CLEAR;
}
else if (per_object_stats_mode == STATS_MODE_READ)
{
instance_stats_mode = SAI_STATS_MODE_READ;
}
else
{
SWSS_LOG_WARN("Stats mode %s not supported for flex counter. Using STATS_MODE_READ_AND_CLEAR", per_object_stats_mode.c_str());
}
effective_stats_mode = (m_groupStatsMode == SAI_STATS_MODE_READ_AND_CLEAR ||
instance_stats_mode == SAI_STATS_MODE_READ_AND_CLEAR) ? SAI_STATS_MODE_READ_AND_CLEAR : SAI_STATS_MODE_READ;
}
else
{
effective_stats_mode = m_groupStatsMode;
}
std::vector<StatType> counter_ids;
for (const auto &str : idStrings)
{
StatType stat;
deserializeStat(str.c_str(), &stat);
counter_ids.push_back(stat);
}
updateSupportedCounters(rid, counter_ids, effective_stats_mode);
std::vector<StatType> supportedIds;
for (auto &counter : counter_ids)
{
if (isCounterSupported(counter))
{
supportedIds.push_back(counter);
}
}
if (supportedIds.empty())
{
SWSS_LOG_NOTICE("%s %s does not has supported counters", m_name.c_str(), sai_serialize_object_id(rid).c_str());
return;
}
if (double_confirm_supported_counters)
{
std::vector<uint64_t> stats(supportedIds.size());
if (!collectData(rid, supportedIds, effective_stats_mode, false, stats))
{
SWSS_LOG_ERROR("%s RID %s can't provide the statistic", m_name.c_str(), sai_serialize_object_id(rid).c_str());
return;
}
}
// Perform a remove and re-add to simplify the logic here
removeObject(vid, false);
bool supportBulk;
// TODO: use if const expression when cpp17 is supported
if (HasStatsMode<CounterIdsType>::value)
{
supportBulk = false;
}
else
{
supportBulk = no_double_check_bulk_capability || checkBulkCapability(vid, rid, supportedIds);
}
if (!supportBulk)
{
auto counter_data = std::make_shared<CounterIds<StatType>>(rid, supportedIds);
// TODO: use if const expression when cpp17 is supported
if (HasStatsMode<CounterIdsType>::value)
{
counter_data->setStatsMode(instance_stats_mode);
}
m_objectIdsMap.emplace(vid, counter_data);
}
else if (m_counterChunkSizeMapFromPrefix.empty())
{
std::sort(supportedIds.begin(), supportedIds.end());
auto bulkContext = getBulkStatsContext(supportedIds, "default", default_bulk_chunk_size);
addBulkStatsContext(vid, rid, supportedIds, *bulkContext.get());
}
else
{
std::map<std::string, vector<StatType>> counter_prefix_map;
std::vector<StatType> 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<StatType>& supportedIds,
_Out_ std::map<std::string, std::vector<StatType>> &counter_prefix_map,
_Out_ std::vector<StatType> &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<BulkContextType> singleBulkContext = it->second;
const std::vector<StatType> &allCounterIds = singleBulkContext.get()->counter_ids;
std::map<std::string, vector<StatType>> counterChunkSizePerPrefix;
std::vector<StatType> 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<sai_object_id_t> oid_set;
std::vector<StatType> counter_ids;
std::shared_ptr<BulkContextType> 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<sai_object_id_t> 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());
}
}
virtual void bulkAddObject(
_In_ const std::vector<sai_object_id_t>& vids,
_In_ const std::vector<sai_object_id_t>& rids,
_In_ const std::vector<std::string>& idStrings,
_In_ const std::string &per_object_stats_mode)
{
SWSS_LOG_ENTER();
sai_stats_mode_t effective_stats_mode;
// TODO: use if const expression when c++17 is supported
if (HasStatsMode<CounterIdsType>::value)
{
// Bulk operation is not supported by the counter group.
SWSS_LOG_INFO("Counter group %s %s does not support bulk. Fallback to single call", m_name.c_str(), m_instanceId.c_str());
// Fall back to old way
for (size_t i = 0; i < vids.size(); i++)
{
auto rid = rids[i];
auto vid = vids[i];
addObject(vid, rid, idStrings, per_object_stats_mode);
}
return;
}
else
{
effective_stats_mode = m_groupStatsMode;
}
std::vector<StatType> allCounterIds, supportedIds;
for (const auto &str : idStrings)
{
StatType stat;
deserializeStat(str.c_str(), &stat);
{
allCounterIds.push_back(stat);
}
}
updateSupportedCounters(rids[0]/*it is not really used*/, allCounterIds, effective_stats_mode);
for (auto stat : allCounterIds)
{
if (isCounterSupported(stat))
{
supportedIds.push_back(stat);
}
}
if (supportedIds.empty())
{
SWSS_LOG_NOTICE("%s %s does not have supported counters", m_name.c_str(), m_instanceId.c_str());
return;
}
std::map<std::vector<StatType>, std::tuple<const std::string, uint32_t>> bulkUnsupportedCounters;
auto statsMode = m_groupStatsMode == SAI_STATS_MODE_READ ? SAI_STATS_MODE_BULK_READ : SAI_STATS_MODE_BULK_READ_AND_CLEAR;
auto checkAndUpdateBulkCapability = [&](const std::vector<StatType> &counter_ids, const std::string &prefix, uint32_t bulk_chunk_size)
{
BulkContextType ctx;
// Check bulk capabilities again
std::vector<StatType> supportedBulkIds;
sai_status_t status = SAI_STATUS_SUCCESS;
if (m_supportedBulkCounters.empty())
{
status = querySupportedCounters(rids[0], statsMode, m_supportedBulkCounters);
}
if (status == SAI_STATUS_SUCCESS && !m_supportedBulkCounters.empty())
{
for (auto stat : counter_ids)
{
if (m_supportedBulkCounters.count(stat) != 0)
{
supportedBulkIds.push_back(stat);
}
}
}
if (supportedBulkIds.size() < counter_ids.size())
{
// Bulk polling is unsupported for the whole group but single polling is supported
// Add all objects to m_objectIdsMap so that they will be polled using single API
for (size_t i = 0; i < vids.size(); i++)
{
auto rid = rids[i];
auto vid = vids[i];
std::vector<uint64_t> stats(counter_ids.size());
if (collectData(rid, counter_ids, effective_stats_mode, false, stats)) {
auto it_vid = m_objectIdsMap.find(vid);
if (it_vid != m_objectIdsMap.end())
{
// Remove and re-add if vid already exists
m_objectIdsMap.erase(it_vid);
}
auto counter_data = std::make_shared<CounterIds<StatType>>(rid, counter_ids);
m_objectIdsMap.emplace(vid, counter_data);
SWSS_LOG_INFO("Fallback to single call for object 0x%" PRIx64, vid);
} else {
SWSS_LOG_WARN("%s RID %s can't provide the statistic", m_name.c_str(), sai_serialize_object_id(rid).c_str());
}
}
return;
}
ctx.counter_ids = counter_ids;
addBulkStatsContext(vids, rids, counter_ids, ctx);
status = m_vendorSai->bulkGetStats(
SAI_NULL_OBJECT_ID,
m_objectType,
static_cast<uint32_t>(ctx.object_keys.size()),
ctx.object_keys.data(),
static_cast<uint32_t>(ctx.counter_ids.size()),
reinterpret_cast<const sai_stat_id_t *>(ctx.counter_ids.data()),
statsMode,
ctx.object_statuses.data(),
ctx.counters.data());
if (status == SAI_STATUS_SUCCESS)
{
auto bulkContext = getBulkStatsContext(counter_ids, prefix, bulk_chunk_size);
addBulkStatsContext(vids, rids, counter_ids, *bulkContext.get());
}
else
{
// Bulk is not supported for this counter prefix
// Append it to bulkUnsupportedCounters
std::tuple<const std::string, uint32_t> value(prefix, bulk_chunk_size);
bulkUnsupportedCounters.emplace(counter_ids, value);
SWSS_LOG_INFO("Counters starting with %s do not support bulk. Fallback to single call for these counters", prefix.c_str());
}
};
if (m_counterChunkSizeMapFromPrefix.empty())
{
std::sort(supportedIds.begin(), supportedIds.end());
checkAndUpdateBulkCapability(supportedIds, "default", default_bulk_chunk_size);
}
else
{
std::map<std::string, vector<StatType>> counter_prefix_map;
std::vector<StatType> default_partition;
createCounterBulkChunkSizePerPrefixPartition(supportedIds, counter_prefix_map, default_partition);
for (auto &counterPrefix : counter_prefix_map)
{
std::sort(counterPrefix.second.begin(), counterPrefix.second.end());
}
std::sort(default_partition.begin(), default_partition.end());
for (auto &counterPrefix : counter_prefix_map)
{
checkAndUpdateBulkCapability(counterPrefix.second, counterPrefix.first, m_counterChunkSizeMapFromPrefix[counterPrefix.first]);
}
checkAndUpdateBulkCapability(default_partition, "default", default_bulk_chunk_size);
}
if (!bulkUnsupportedCounters.empty())
{
SWSS_LOG_NOTICE("Partial counters do not support bulk. Re-check bulk capability for each object");
for (auto &it : bulkUnsupportedCounters)
{
std::vector<sai_object_id_t> bulkSupportedRIDs;
std::vector<sai_object_id_t> bulkSupportedVIDs;
for (size_t i = 0; i < vids.size(); i++)
{
auto rid = rids[i];
auto vid = vids[i];
std::vector<uint64_t> stats(it.first.size());
if (checkBulkCapability(vid, rid, it.first))
{
bulkSupportedVIDs.push_back(vid);
bulkSupportedRIDs.push_back(rid);
}
else if (!double_confirm_supported_counters || collectData(rid, it.first, effective_stats_mode, false, stats))
{
SWSS_LOG_INFO("Fallback to single call for object 0x%" PRIx64, vid);
auto it_vid = m_objectIdsMap.find(vid);
if (it_vid != m_objectIdsMap.end())
{
// Remove and re-add if vid already exists
m_objectIdsMap.erase(it_vid);
}
auto counter_data = std::make_shared<CounterIds<StatType>>(rid, supportedIds);
m_objectIdsMap.emplace(vid, counter_data);
}
else
{
SWSS_LOG_WARN("%s RID %s can't provide the statistic", m_name.c_str(), sai_serialize_object_id(rid).c_str());
}
}
if (!bulkSupportedVIDs.empty() && !bulkSupportedRIDs.empty())
{
auto bulkContext = getBulkStatsContext(it.first, get<0>(it.second), get<1>(it.second));
addBulkStatsContext(bulkSupportedVIDs, bulkSupportedRIDs, it.first, *bulkContext.get());
}
}
}
}
void removeObject(
_In_ sai_object_id_t vid) override
{
SWSS_LOG_ENTER();
removeObject(vid, true);
}
void removeObject(
_In_ sai_object_id_t vid,
_In_ bool log)
{
SWSS_LOG_ENTER();
auto iter = m_objectIdsMap.find(vid);
if (iter != m_objectIdsMap.end())
{
m_objectIdsMap.erase(iter);
}
// An object can be in both m_objectIdsMap and the bulk context
// when bulk polling is supported by some counter prefixes but unsupported by some others
if (!removeBulkStatsContext(vid) && log)
{
SWSS_LOG_NOTICE("Trying to remove nonexisting %s %s",
sai_serialize_object_type(m_objectType).c_str(),
sai_serialize_object_id(vid).c_str());
}
}
virtual void collectData(
_In_ swss::Table &countersTable) override
{
SWSS_LOG_ENTER();
sai_stats_mode_t effective_stats_mode = m_groupStatsMode;
for (const auto &kv : m_objectIdsMap)
{
const auto &vid = kv.first;
const auto &rid = kv.second->rid;
const auto &statIds = kv.second->counter_ids;
// TODO: use if const expression when cpp17 is supported
if (HasStatsMode<CounterIdsType>::value)
{
effective_stats_mode = (m_groupStatsMode == SAI_STATS_MODE_READ_AND_CLEAR ||
kv.second->getStatsMode() == SAI_STATS_MODE_READ_AND_CLEAR) ? SAI_STATS_MODE_READ_AND_CLEAR : SAI_STATS_MODE_READ;
}
std::vector<uint64_t> stats(statIds.size());
if (!collectData(rid, statIds, effective_stats_mode, true, stats))
{
continue;
}
std::vector<swss::FieldValueTuple> values;
for (size_t i = 0; i != statIds.size(); i++)
{
values.emplace_back(serializeStat(statIds[i]), std::to_string(stats[i]));
}
countersTable.set(sai_serialize_object_id(vid), values, "");
}
for (const auto &kv : m_bulkContexts)
{
bulkCollectData(countersTable, *kv.second.get());
}
}
void runPlugin(
_In_ swss::DBConnector& counters_db,
_In_ const std::vector<std::string>& argv) override
{
SWSS_LOG_ENTER();
if (!hasObject())
{
return;
}
SWSS_LOG_DEBUG("Before running plugin %s %s", m_instanceId.c_str(), m_name.c_str());
std::vector<std::string> idStrings;
idStrings.reserve(m_objectIdsMap.size());
std::transform(m_objectIdsMap.begin(),
m_objectIdsMap.end(),
std::back_inserter(idStrings),
[] (auto &kv) { return sai_serialize_object_id(kv.first); });
for (auto &kv : m_bulkContexts)
{
std::transform(kv.second->object_vids.begin(),
kv.second->object_vids.end(),
std::back_inserter(idStrings),
[] (auto &vid) { return sai_serialize_object_id(vid); });
}
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
{
SWSS_LOG_ENTER();
return !m_objectIdsMap.empty() || !m_bulkContexts.empty();
}
private:
bool isCounterSupported(
_In_ StatType counter) const
{
SWSS_LOG_ENTER();
return m_supportedCounters.count(counter) != 0;
}
bool collectData(
_In_ sai_object_id_t rid,
_In_ const std::vector<StatType> &counter_ids,
_In_ sai_stats_mode_t stats_mode,
_In_ bool log_err,
_Out_ std::vector<uint64_t> &stats)
{
SWSS_LOG_ENTER();
sai_status_t status;
if (!use_sai_stats_ext)
{
status = m_vendorSai->getStats(
m_objectType,
rid,
static_cast<uint32_t>(counter_ids.size()),
(const sai_stat_id_t *)counter_ids.data(),
stats.data());
if (status != SAI_STATUS_SUCCESS)
{
if (log_err)
SWSS_LOG_ERROR("Failed to get stats of %s 0x%" PRIx64 ": %d", m_name.c_str(), rid, status);
else
SWSS_LOG_INFO("Failed to get stats of %s 0x%" PRIx64 ": %d", m_name.c_str(), rid, status);
return false;
}
if (stats_mode == SAI_STATS_MODE_READ_AND_CLEAR)
{
status = m_vendorSai->clearStats(
m_objectType,
rid,
static_cast<uint32_t>(counter_ids.size()),
reinterpret_cast<const sai_stat_id_t *>(counter_ids.data()));
if (status != SAI_STATUS_SUCCESS)
{
if (log_err)
{
SWSS_LOG_ERROR("%s: failed to clear stats %s, rv: %s",
m_name.c_str(),
sai_serialize_object_id(rid).c_str(),
sai_serialize_status(status).c_str());
}
else
{
SWSS_LOG_INFO("%s: failed to clear stats %s, rv: %s",
m_name.c_str(),
sai_serialize_object_id(rid).c_str(),
sai_serialize_status(status).c_str());
}
return false;
}
}
}
else
{
status = m_vendorSai->getStatsExt(
m_objectType,
rid,
static_cast<uint32_t>(counter_ids.size()),
reinterpret_cast<const sai_stat_id_t *>(counter_ids.data()),
stats_mode,
stats.data());
if (status != SAI_STATUS_SUCCESS)
{
if (log_err)
{
SWSS_LOG_ERROR("Failed to get stats of %s 0x%" PRIx64 ": %d", m_name.c_str(), rid, status);
}
else
{
SWSS_LOG_INFO("Failed to get stats of %s 0x%" PRIx64 ": %d", m_name.c_str(), rid, status);
}
return false;
}
}
return true;
}
void bulkCollectData(
_In_ swss::Table &countersTable,
_Inout_ BulkContextType &ctx)
{
SWSS_LOG_ENTER();
auto statsMode = m_groupStatsMode == SAI_STATS_MODE_READ ? SAI_STATS_MODE_BULK_READ : SAI_STATS_MODE_BULK_READ_AND_CLEAR;
uint32_t bulk_chunk_size = ctx.default_bulk_chunk_size;
uint32_t size = static_cast<uint32_t>(ctx.object_keys.size());
if (bulk_chunk_size > size || bulk_chunk_size == 0)
{
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<uint32_t>(ctx.counter_ids.size()),
reinterpret_cast<const sai_stat_id_t *>(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 %s %s %s starting object %u bulk chunk size %u: %d",
m_instanceId.c_str(), m_name.c_str(), ctx.name.c_str(), sai_serialize_object_type(m_objectType).c_str(), current, bulk_chunk_size, 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<swss::FieldValueTuple> values;
for (size_t i = 0; i < ctx.object_keys.size(); i++)
{
if (SAI_STATUS_SUCCESS != ctx.object_statuses[i])
{
SWSS_LOG_ERROR("Failed to get stats of %s 0x%" PRIx64 " 0x%" PRIx64 ": %d", m_name.c_str(), ctx.object_vids[i], ctx.object_keys[i].key.object_id, ctx.object_statuses[i]);
continue;
}
const auto &vid = ctx.object_vids[i];
for (size_t j = 0; j < ctx.counter_ids.size(); j++)
{
values.emplace_back(serializeStat(ctx.counter_ids[j]), std::to_string(ctx.counters[i * ctx.counter_ids.size() + j]));
}
countersTable.set(sai_serialize_object_id(vid), values, "");
values.clear();
}
// First generate the key, then replace spaces with underscores to avoid issues when Lua plugins handle the timestamp
std::string timestamp_key = m_instanceId + "_" + m_name + "_time_stamp";
std::replace(timestamp_key.begin(), timestamp_key.end(), ' ', '_');
values.emplace_back(timestamp_key, std::to_string(time_stamp));
countersTable.set("TIME_STAMP", values, "");
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<StatType>& counterIds,
_In_ const std::string& name,
_In_ uint32_t bulk_chunk_size=0)
{
SWSS_LOG_ENTER();
auto iter = m_bulkContexts.find(counterIds);
if (iter != m_bulkContexts.end())
{
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<BulkContextType>());
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<StatType>& counterIds,
_In_ const std::shared_ptr<BulkContextType> 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,
_In_ const std::vector<StatType>& counterIds,
_Inout_ BulkContextType &ctx)
{
SWSS_LOG_ENTER();
ctx.object_vids.push_back(vid);
sai_object_key_t object_key;
object_key.key.object_id = rid;
ctx.object_keys.push_back(object_key);
ctx.object_statuses.push_back(SAI_STATUS_SUCCESS);
ctx.counters.resize(counterIds.size() * ctx.object_keys.size());
}
void addBulkStatsContext(
_In_ const std::vector<sai_object_id_t> &vids,
_In_ const std::vector<sai_object_id_t> &rids,
_In_ const std::vector<StatType>& counterIds,
_Inout_ BulkContextType &ctx)
{
SWSS_LOG_ENTER();
ctx.object_vids.insert(ctx.object_vids.end(), vids.begin(), vids.end());
transform(rids.begin(), rids.end(), back_inserter(ctx.object_keys), [](sai_object_id_t rid) {
sai_object_key_t key;
key.key.object_id = rid;
return key;
});
ctx.object_statuses.insert(ctx.object_statuses.end(), vids.size(), SAI_STATUS_SUCCESS);
ctx.counters.resize(counterIds.size() * ctx.object_keys.size());
}
bool removeBulkStatsContext(
_In_ sai_object_id_t vid)
{
SWSS_LOG_ENTER();
std::set<std::vector<StatType>> bulkContextsToBeRemoved;
bool found = false;
for (auto iter = m_bulkContexts.begin(); iter != m_bulkContexts.end(); iter++)
{
auto &ctx = *iter->second.get();
auto vid_iter = std::find(ctx.object_vids.begin(), ctx.object_vids.end(), vid);
if (vid_iter == ctx.object_vids.end())
{
continue;
}
found = true;
auto index = std::distance(ctx.object_vids.begin(), vid_iter);
ctx.object_vids.erase(vid_iter);
if (ctx.object_vids.empty())
{
// 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
{
auto key_iter = ctx.object_keys.begin();
std::advance(key_iter, index);
ctx.object_keys.erase(key_iter);
ctx.counters.resize(ctx.counter_ids.size() * ctx.object_keys.size());
ctx.object_statuses.pop_back();
}
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;
}
bool checkBulkCapability(
_In_ sai_object_id_t vid,
_In_ sai_object_id_t rid,
_In_ const std::vector<StatType>& counter_ids)
{
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(
SAI_NULL_OBJECT_ID,
m_objectType,
static_cast<uint32_t>(ctx.object_keys.size()),
ctx.object_keys.data(),
static_cast<uint32_t>(ctx.counter_ids.size()),
reinterpret_cast<const sai_stat_id_t *>(ctx.counter_ids.data()),
statsMode,
ctx.object_statuses.data(),
ctx.counters.data());
return status == SAI_STATUS_SUCCESS;
}
void updateSupportedCounters(
_In_ sai_object_id_t rid,
_In_ const std::vector<StatType>& counter_ids,
_In_ sai_stats_mode_t stats_mode)
{
SWSS_LOG_ENTER();
if (!m_supportedCounters.empty() && !always_check_supported_counters)
{
SWSS_LOG_NOTICE("Ignore checking of supported counters");
return;
}
if (always_check_supported_counters && !dont_clear_support_counter)
{
m_supportedCounters.clear();
}
if (!use_sai_stats_capa_query || querySupportedCounters(rid, stats_mode, m_supportedCounters) != SAI_STATUS_SUCCESS)
{
/* Fallback to legacy approach */
getSupportedCounters(rid, counter_ids, stats_mode);
}
}
sai_status_t querySupportedCounters(
_In_ sai_object_id_t rid,
_In_ sai_stats_mode_t stats_mode,
_Out_ std::set<StatType> &supportedCounters)
{
SWSS_LOG_ENTER();
sai_stat_capability_list_t stats_capability;
stats_capability.count = 0;
stats_capability.list = nullptr;
if (m_switchId == SAI_NULL_OBJECT_ID)
{
m_switchId = m_vendorSai->switchIdQuery(rid);
}
/* First call is to check the size needed to allocate */
sai_status_t status = m_vendorSai->queryStatsCapability(
m_switchId,
m_objectType,
&stats_capability);
/* Second call is for query statistics capability */
if (status == SAI_STATUS_BUFFER_OVERFLOW)
{
std::vector<sai_stat_capability_t> statCapabilityList(stats_capability.count);
stats_capability.list = statCapabilityList.data();
status = m_vendorSai->queryStatsCapability(
m_switchId,
m_objectType,
&stats_capability);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_INFO("Unable to get %s supported counters for %s",
m_name.c_str(),
sai_serialize_object_id(rid).c_str());
}
else
{
for (auto statCapability: statCapabilityList)
{
auto currentStatModes = statCapability.stat_modes;
if (!(currentStatModes & stats_mode))
{
continue;
}
StatType counter = static_cast<StatType>(statCapability.stat_enum);
supportedCounters.insert(counter);
}
}
}
return status;
}
void getSupportedCounters(
_In_ sai_object_id_t rid,
_In_ const std::vector<StatType>& counter_ids,
_In_ sai_stats_mode_t stats_mode)
{
SWSS_LOG_ENTER();
std::vector<uint64_t> values(1);
for (const auto &counter : counter_ids)
{
if (isCounterSupported(counter))
{
continue;
}
std::vector<StatType> tmp_counter_ids {counter};
if (!collectData(rid, tmp_counter_ids, stats_mode, false, values))
{
continue;
}
m_supportedCounters.insert(counter);
}
}
protected:
sai_object_type_t m_objectType;
sairedis::SaiInterface *m_vendorSai;
sai_stats_mode_t& m_groupStatsMode;
std::set<StatType> m_supportedCounters;
std::set<StatType> m_supportedBulkCounters;
std::map<sai_object_id_t, std::shared_ptr<CounterIdsType>> m_objectIdsMap;
std::map<std::vector<StatType>, std::shared_ptr<BulkContextType>> m_bulkContexts;
};
template <typename AttrType>
class AttrContext : public CounterContext<AttrType>
{
public:
typedef CounterIds<AttrType> AttrIdsType;
typedef CounterContext<AttrType> 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<AttrType>(name, instance, object_type, vendor_sai, stats_mode)
{
SWSS_LOG_ENTER();
}
void addObject(
_In_ sai_object_id_t vid,
_In_ sai_object_id_t rid,
_In_ const std::vector<std::string> &idStrings,
_In_ const std::string &per_object_stats_mode) override
{
SWSS_LOG_ENTER();
std::vector<AttrType> attrIds;
for (const auto &str : idStrings)
{
AttrType attr;
deserializeAttr(str, attr);
attrIds.push_back(attr);
}
auto it = Base::m_objectIdsMap.find(vid);
if (it != Base::m_objectIdsMap.end())
{
it->second->counter_ids = attrIds;
return;
}
auto attr_ids = std::make_shared<AttrIdsType>(rid, attrIds);
Base::m_objectIdsMap.emplace(vid, attr_ids);
}
void bulkAddObject(
_In_ const std::vector<sai_object_id_t>& vids,
_In_ const std::vector<sai_object_id_t>& rids,
_In_ const std::vector<std::string>& idStrings,
_In_ const std::string &per_object_stats_mode) override
{
SWSS_LOG_ENTER();
for (auto i = 0uL; i < vids.size(); i++)
{
addObject(vids[i], rids[i], idStrings, per_object_stats_mode);
}
}
void collectData(
_In_ swss::Table &countersTable) override
{
SWSS_LOG_ENTER();
for (const auto &kv : Base::m_objectIdsMap)
{
const auto &vid = kv.first;
const auto &rid = kv.second->rid;
const auto &attrIds = kv.second->counter_ids;
std::vector<sai_attribute_t> attrs(attrIds.size());
for (size_t i = 0; i < attrIds.size(); i++)
{
attrs[i].id = attrIds[i];
}
// Get attr
sai_status_t status = Base::m_vendorSai->get(
Base::m_objectType,
rid,
static_cast<uint32_t>(attrIds.size()),
attrs.data());
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to get attr of %s 0x%" PRIx64 ": %d",
sai_serialize_object_type(Base::m_objectType).c_str(), vid, status);
continue;
}
std::vector<swss::FieldValueTuple> values;
for (size_t i = 0; i != attrIds.size(); i++)
{
auto meta = sai_metadata_get_attr_metadata(Base::m_objectType, attrs[i].id);
if (!meta)
{
SWSS_LOG_THROW("Failed to get metadata for %s", sai_serialize_object_type(Base::m_objectType).c_str());
}
values.emplace_back(meta->attridname, sai_serialize_attr_value(*meta, attrs[i]));
}
countersTable.set(sai_serialize_object_id(vid), values, "");
}
}
};
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, instance), m_dbCounters(dbCounters), m_vendorSai(vendor_sai)
{
SWSS_LOG_ENTER();
}
void addObject(
_In_ sai_object_id_t vid,
_In_ sai_object_id_t rid,
_In_ const std::vector<std::string> &idStrings,
_In_ const std::string &per_object_stats_mode) override
{
SWSS_LOG_ENTER();
if (m_switchId == 0UL)
{
m_switchId = m_vendorSai->switchIdQuery(rid);
}
if (m_meterBucketsPerEni == 0)
{
if (m_initalized) {
// need not repeat these global checks for each object
return;
}
sai_attribute_t attr;
attr.id = SAI_SWITCH_ATTR_DASH_CAPS_MAX_METER_BUCKET_COUNT_PER_ENI;
auto status = m_vendorSai->get(SAI_OBJECT_TYPE_SWITCH, m_switchId,
1, &attr);
if ((status != SAI_STATUS_SUCCESS) ||
(attr.value.u32 == 0))
{
m_initalized = true;
SWSS_LOG_NOTICE("No meter buckets supported per ENI for %s %s",
m_name.c_str(), sai_serialize_object_id(rid).c_str());
return;
}
m_meterBucketsPerEni = attr.value.u32;
}
if (m_supportedMeterCounters.empty())
{
if (m_initalized) {
// need not repeat these global checks for each object
return;
}
getSupportedMeterCounters(rid, idStrings);
if (m_supportedMeterCounters.empty())
{
m_initalized = true;
SWSS_LOG_NOTICE("%s %s does not have supported counters",
m_name.c_str(), sai_serialize_object_id(rid).c_str());
return;
}
if (!checkBulkCapability(vid, rid, m_supportedMeterCounters, m_groupStatsMode)) {
m_initalized = true;
SWSS_LOG_NOTICE("%s %s does not have bulk get support for Dash meter counters",
m_name.c_str(), sai_serialize_object_id(rid).c_str());
return;
}
}
// add object to flex counter poll
addBulkMeterContext(vid, rid);
m_initalized = true;
}
void removeObject(_In_ sai_object_id_t vid) override
{
SWSS_LOG_ENTER();
auto it = m_bulkMeterContexts.find(vid);
if (it == m_bulkMeterContexts.end()) {
return;
}
// delete all meter bucket stats for this object from counters DB
swss::DBConnector db(m_dbCounters, 0);
swss::RedisPipeline pipeline(&db);
swss::Table countersTable(&pipeline, COUNTERS_TABLE, true);
for (const auto& object_key: it->second.object_keys) {
countersTable.del(sai_serialize_meter_bucket_entry(object_key.key.meter_bucket_entry));
}
// remove from flex counter poll
m_bulkMeterContexts.erase(it);
}
void collectData(_In_ swss::Table &countersTable) override
{
SWSS_LOG_ENTER();
for (auto &kv : m_bulkMeterContexts)
{
bulkCollectData(countersTable, kv.second);
}
}
void runPlugin(
_In_ swss::DBConnector& counters_db,
_In_ const std::vector<std::string>& argv) override
{
SWSS_LOG_ENTER();
if (!hasObject())
{
return;
}
for (auto &kv : m_bulkMeterContexts)
{
auto& ctx = kv.second;
std::vector<std::string> idStrings;
idStrings.reserve(m_meterBucketsPerEni);
for (uint32_t i = 0; i < m_meterBucketsPerEni; ++i) {
idStrings.push_back(sai_serialize_meter_bucket_entry(ctx.object_keys[i].key.meter_bucket_entry));
}
std::for_each(m_plugins.begin(),
m_plugins.end(),
[&] (auto &sha) { runRedisScript(counters_db, sha, idStrings, argv); });
}
}
void bulkAddObject(
_In_ const std::vector<sai_object_id_t>& vids,
_In_ const std::vector<sai_object_id_t>& rids,
_In_ const std::vector<std::string>& idStrings,
_In_ const std::string &per_object_stats_mode) override
{
SWSS_LOG_ENTER();
for (auto i = 0uL; i < vids.size(); i++)
{
addObject(vids[i], rids[i], idStrings, per_object_stats_mode);
}
}
bool hasObject() const override
{
SWSS_LOG_ENTER();
return !m_bulkMeterContexts.empty();
}
private:
struct BulkMeterStatsContext
{
sai_object_id_t eni_vid;
std::vector<sai_object_key_t> object_keys;
std::vector<sai_status_t> object_statuses;
std::vector<sai_meter_bucket_entry_stat_t> counter_ids;
std::vector<uint64_t> counters;
};
bool bulkCollectData(
_In_ swss::Table &countersTable,
_Inout_ BulkMeterStatsContext &ctx)
{
SWSS_LOG_ENTER();
if (ctx.object_keys.size() == 0)
{
return false;
}
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<uint32_t>(ctx.object_keys.size()),
ctx.object_keys.data(),
static_cast<uint32_t>(ctx.counter_ids.size()),
reinterpret_cast<const sai_stat_id_t *>(ctx.counter_ids.data()),
statsMode,
ctx.object_statuses.data(),
ctx.counters.data());
if (SAI_STATUS_SUCCESS != status)
{
SWSS_LOG_WARN("Failed to get meter bulk stats for %s: ENI 0x% " PRIx64 ": %u", m_name.c_str(), ctx.eni_vid, status);
return false;
}
bool meter_class_hit = false;
std::vector<swss::FieldValueTuple> values;
for (size_t i = 0; i < ctx.object_keys.size(); i++)
{
if (SAI_STATUS_SUCCESS != ctx.object_statuses[i])
{
SWSS_LOG_ERROR("Failed to get meter bulk stats of %s for ENI 0x%" PRIx64 " meter-class %d : %d",
m_name.c_str(), ctx.object_keys[i].key.meter_bucket_entry.eni_id,
ctx.object_keys[i].key.meter_bucket_entry.meter_class,
ctx.object_statuses[i]);
continue;
}
for (size_t j = 0; j < ctx.counter_ids.size(); ++j) {
if (ctx.counters[i * ctx.counter_ids.size() + j] != 0UL) {
meter_class_hit = true;
break;
}
}
// write only non-zero meter classes to COUNTERS_DB
if (!meter_class_hit) {
continue;
}
meter_class_hit = false;
for (size_t j = 0; j < ctx.counter_ids.size(); j++)
{
values.emplace_back(serializeStat(ctx.counter_ids[j]), std::to_string(ctx.counters[i * ctx.counter_ids.size() + j]));
}
countersTable.set(sai_serialize_meter_bucket_entry(ctx.object_keys[i].key.meter_bucket_entry), values, "");
values.clear();
}
return true;
}
bool checkBulkCapability(
_In_ sai_object_id_t vid,
_In_ sai_object_id_t rid,
_In_ const std::vector<sai_meter_bucket_entry_stat_t>& counter_ids,
_In_ sai_stats_mode_t stats_mode)
{
SWSS_LOG_ENTER();
auto ctx = makeBulkMeterContext(vid, rid);
auto statsMode = stats_mode == 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<uint32_t>(ctx.object_keys.size()),
ctx.object_keys.data(),
static_cast<uint32_t>(ctx.counter_ids.size()),
reinterpret_cast<const sai_stat_id_t *>(ctx.counter_ids.data()),
statsMode,
ctx.object_statuses.data(),
ctx.counters.data());
return status == SAI_STATUS_SUCCESS;
}
void updateSupportedMeterCounters(
_In_ sai_object_id_t rid,
_In_ const std::vector<sai_meter_bucket_entry_stat_t>& counter_ids,
_In_ sai_stats_mode_t stats_mode)
{
SWSS_LOG_ENTER();
if (!m_supportedMeterCounters.empty() && !always_check_supported_counters)
{
SWSS_LOG_NOTICE("Ignore checking of supported counters");
return;
}
querySupportedMeterCounters(rid, counter_ids, stats_mode);
}
sai_status_t querySupportedMeterCounters(
_In_ sai_object_id_t rid,
_In_ const std::vector<sai_meter_bucket_entry_stat_t>& counter_ids,
_In_ sai_stats_mode_t stats_mode)
{
SWSS_LOG_ENTER();
sai_stat_capability_list_t stats_capability;
stats_capability.count = 0;
stats_capability.list = nullptr;
/* First call is to check the size needed to allocate */
sai_status_t status = m_vendorSai->queryStatsCapability(m_switchId,
m_objectType,
&stats_capability);
/* Second call is for query statistics capability */
if (status == SAI_STATUS_BUFFER_OVERFLOW)
{
std::vector<sai_stat_capability_t> statCapabilityList(stats_capability.count);
stats_capability.list = statCapabilityList.data();
status = m_vendorSai->queryStatsCapability(m_switchId,
m_objectType,
&stats_capability);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_INFO("Unable to get %s supported meter counters for %s",
m_name.c_str(),
sai_serialize_object_id(rid).c_str());
}
else
{
for (auto counter: counter_ids) {
for (auto statCapability: statCapabilityList)
{
auto currentStatModes = statCapability.stat_modes;
if (!(currentStatModes & stats_mode))
{
continue;
}
if (counter == static_cast<sai_meter_bucket_entry_stat_t>(statCapability.stat_enum)) {
m_supportedMeterCounters.push_back(counter);
}
}
}
}
}
return status;
}
void getSupportedMeterCounters(sai_object_id_t rid,
const std::vector<std::string> &idStrings)
{
SWSS_LOG_ENTER();
std::vector<sai_meter_bucket_entry_stat_t> counter_ids;
for (const auto &str : idStrings)
{
SWSS_LOG_INFO("id string %s", str.c_str());
sai_meter_bucket_entry_stat_t stat;
deserializeStat(str.c_str(), &stat);
counter_ids.push_back(stat);
}
updateSupportedMeterCounters(rid, counter_ids, m_groupStatsMode);
}
BulkMeterStatsContext makeBulkMeterContext(sai_object_id_t vid, sai_object_id_t rid)
{
SWSS_LOG_ENTER();
BulkMeterStatsContext ctx;
ctx.eni_vid = vid;
sai_object_key_t object_key;
object_key.key.meter_bucket_entry.eni_id = rid;
object_key.key.meter_bucket_entry.switch_id = m_switchId;
for (uint32_t i = 0; i < m_meterBucketsPerEni; ++i) {
object_key.key.meter_bucket_entry.meter_class = i;
ctx.object_keys.push_back(object_key);
}
ctx.object_statuses.resize(ctx.object_keys.size());
ctx.counter_ids = m_supportedMeterCounters;
ctx.counters.resize(m_supportedMeterCounters.size() * ctx.object_keys.size());
return ctx;
}
void addBulkMeterContext(sai_object_id_t vid, sai_object_id_t rid)
{
SWSS_LOG_ENTER();
auto it = m_bulkMeterContexts.find(vid);
if (it != m_bulkMeterContexts.end()) {
return;
}
m_bulkMeterContexts.emplace(vid, makeBulkMeterContext(vid, rid));
}
std::map<sai_object_id_t, BulkMeterStatsContext> m_bulkMeterContexts;
std::vector<sai_meter_bucket_entry_stat_t> m_supportedMeterCounters;
sai_object_type_t m_objectType = (sai_object_type_t) SAI_OBJECT_TYPE_METER_BUCKET_ENTRY;
std::string m_dbCounters;
sairedis::SaiInterface *m_vendorSai;
sai_stats_mode_t m_groupStatsMode = SAI_STATS_MODE_READ;
sai_object_id_t m_switchId = 0UL;
uint32_t m_meterBucketsPerEni = 0;
bool m_initalized = false;
};
FlexCounter::FlexCounter(
_In_ const std::string& instanceId,
_In_ std::shared_ptr<sairedis::SaiInterface> vendorSai,
_In_ const std::string& dbCounters,
_In_ const bool noDoubleCheckBulkCapability):
m_readyToPoll(false),
m_pollInterval(0),
m_instanceId(instanceId),
m_vendorSai(vendorSai),
m_dbCounters(dbCounters),
m_noDoubleCheckBulkCapability(noDoubleCheckBulkCapability)
{
SWSS_LOG_ENTER();
m_enable = false;
m_isDiscarded = false;
startFlexCounterThread();
}
FlexCounter::~FlexCounter(void)
{
SWSS_LOG_ENTER();
endFlexCounterThread();
}
void FlexCounter::setPollInterval(
_In_ uint32_t pollInterval)
{
SWSS_LOG_ENTER();
m_pollInterval = pollInterval;
}
void FlexCounter::setStatus(
_In_ const std::string& status)
{
SWSS_LOG_ENTER();
if (status == "enable")
{
m_enable = true;
}
else if (status == "disable")
{
m_enable = false;
}
else
{
SWSS_LOG_WARN("Input value %s is not supported for Flex counter status, enter enable or disable", status.c_str());
}
}
void FlexCounter::setStatsMode(
_In_ const std::string& mode)
{
SWSS_LOG_ENTER();
if (mode == STATS_MODE_READ)
{
m_statsMode = SAI_STATS_MODE_READ;
SWSS_LOG_DEBUG("Set STATS MODE %s for FC %s", mode.c_str(), m_instanceId.c_str());
}
else if (mode == STATS_MODE_READ_AND_CLEAR)
{
m_statsMode = SAI_STATS_MODE_READ_AND_CLEAR;
SWSS_LOG_DEBUG("Set STATS MODE %s for FC %s", mode.c_str(), m_instanceId.c_str());
}
else
{
SWSS_LOG_WARN("Input value %s is not supported for Flex counter stats mode, enter STATS_MODE_READ or STATS_MODE_READ_AND_CLEAR", mode.c_str());
}
}
void FlexCounter::removeDataFromCountersDB(
_In_ sai_object_id_t vid,
_In_ const std::string &ratePrefix)
{
SWSS_LOG_ENTER();
swss::DBConnector db(m_dbCounters, 0);
swss::RedisPipeline pipeline(&db);
swss::Table countersTable(&pipeline, COUNTERS_TABLE, false);
std::string vidStr = sai_serialize_object_id(vid);
countersTable.del(vidStr);
if (!ratePrefix.empty())
{
swss::Table ratesTable(&pipeline, RATES_TABLE, false);
ratesTable.del(vidStr);
ratesTable.del(vidStr + ratePrefix);
}
}
void FlexCounter::removeCounterPlugins()
{
MUTEX;
SWSS_LOG_ENTER();
for (const auto &kv : m_counterContext)
{
kv.second->removePlugins();
}
m_isDiscarded = true;
}
void FlexCounter::addCounterPlugin(
_In_ const std::vector<swss::FieldValueTuple>& values)
{
MUTEX;
SWSS_LOG_ENTER();
m_isDiscarded = false;
uint32_t bulkChunkSize = 0;
std::string bulkChunkSizePerPrefix;
for (auto& fvt: values)
{
auto& field = fvField(fvt);
auto& value = fvValue(fvt);
auto shaStrings = swss::tokenize(value, ',');
if (field == POLL_INTERVAL_FIELD)
{
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);
}
else if (field == STATS_MODE_FIELD)
{
setStatsMode(value);
}
else
{
auto counterTypeRef = m_plugIn2CounterType.find(field);
if (counterTypeRef != m_plugIn2CounterType.end())
{
getCounterContext(counterTypeRef->second)->addPlugins(shaStrings);
if (m_noDoubleCheckBulkCapability)
{
getCounterContext(counterTypeRef->second)->setNoDoubleCheckBulkCapability(true);
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
{
SWSS_LOG_ERROR("Field is not supported %s", field.c_str());
}
}
}
// notify thread to start polling
notifyPoll();
}
bool FlexCounter::isEmpty()
{
MUTEX;
SWSS_LOG_ENTER();
return allIdsEmpty() && allPluginsEmpty();
}
bool FlexCounter::isDiscarded()
{
SWSS_LOG_ENTER();
return isEmpty() && m_isDiscarded;
}
bool FlexCounter::allIdsEmpty() const
{
SWSS_LOG_ENTER();
for (auto &kv : m_counterContext)
{
if (kv.second->hasObject())
{
return false;
}
}
return true;
}
bool FlexCounter::allPluginsEmpty() const
{
SWSS_LOG_ENTER();
for (auto &kv : m_counterContext)
{
if (kv.second->hasPlugin())
{
return false;
}
}
return true;
}
std::shared_ptr<BaseCounterContext> FlexCounter::createCounterContext(
_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<CounterContext<sai_port_stat_t>>(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_WRED_ECN_PORT)
{
auto context = std::make_shared<CounterContext<sai_port_stat_t>>(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<CounterContext<sai_port_stat_t>>(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;
return context;
}
else if (context_name == COUNTER_TYPE_QUEUE)
{
auto context = std::make_shared<CounterContext<sai_queue_stat_t>>(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_WRED_ECN_QUEUE)
{
auto context = std::make_shared<CounterContext<sai_queue_stat_t>>(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<CounterContext<sai_ingress_priority_group_stat_t>>(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<CounterContext<sai_router_interface_stat_t>>(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<CounterContext<sai_switch_stat_t>>(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;
return context;
}
else if (context_name == COUNTER_TYPE_MACSEC_FLOW)
{
auto context = std::make_shared<CounterContext<sai_macsec_flow_stat_t>>(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<CounterContext<sai_macsec_sa_stat_t>>(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;
return context;
}
else if (context_name == COUNTER_TYPE_FLOW)
{
auto context = std::make_shared<CounterContext<sai_counter_stat_t>>(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;
return context;
}
else if (context_name == COUNTER_TYPE_TUNNEL)
{
auto context = std::make_shared<CounterContext<sai_tunnel_stat_t>>(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<CounterContext<sai_buffer_pool_stat_t>>(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<CounterContext<sai_eni_stat_t>>(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<DashMeterCounterContext>(context_name, instance, m_vendorSai.get(), m_dbCounters);
}
else if (context_name == ATTR_TYPE_QUEUE)
{
return std::make_shared<AttrContext<sai_queue_attr_t>>(context_name, instance, SAI_OBJECT_TYPE_QUEUE, m_vendorSai.get(), m_statsMode);
}
else if (context_name == ATTR_TYPE_PG)
{
return std::make_shared<AttrContext<sai_ingress_priority_group_attr_t>>(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<AttrContext<sai_macsec_sa_attr_t>>(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<AttrContext<sai_acl_counter_attr_t>>(context_name, instance, SAI_OBJECT_TYPE_ACL_COUNTER, m_vendorSai.get(), m_statsMode);
}
else if (context_name == COUNTER_TYPE_POLICER)
{
return std::make_shared<CounterContext<sai_policer_stat_t>>(context_name, instance, SAI_OBJECT_TYPE_POLICER, m_vendorSai.get(), m_statsMode);
}
else if (context_name == COUNTER_TYPE_SRV6)
{
return std::make_shared<CounterContext<sai_counter_stat_t>>(context_name, instance, SAI_OBJECT_TYPE_COUNTER, m_vendorSai.get(), m_statsMode);
}
SWSS_LOG_THROW("Invalid counter type %s", context_name.c_str());
// GCC 8.3 requires a return value here
return nullptr;
}
std::shared_ptr<BaseCounterContext> FlexCounter::getCounterContext(
_In_ const std::string &name)
{
SWSS_LOG_ENTER();
auto iter = m_counterContext.find(name);
if (iter != m_counterContext.end())
{
return iter->second;
}
auto counterContext = createCounterContext(name, m_instanceId);
if (m_noDoubleCheckBulkCapability)
{
counterContext->setNoDoubleCheckBulkCapability(true);
SWSS_LOG_NOTICE("Do not double check bulk capability counter context %s %s", m_instanceId.c_str(), name.c_str());
}
auto ret = m_counterContext.emplace(name, counterContext);
return ret.first->second;
}
void FlexCounter::removeCounterContext(
_In_ const std::string &name)
{
SWSS_LOG_ENTER();
auto iter = m_counterContext.find(name);
if (iter != m_counterContext.end())
{
m_counterContext.erase(iter);
}
else
{
SWSS_LOG_ERROR("Try to remove non-exist counter context %s", name.c_str());
}
}
bool FlexCounter::hasCounterContext(
_In_ const std::string &name) const
{
SWSS_LOG_ENTER();
return m_counterContext.find(name) != m_counterContext.end();
}
void FlexCounter::collectCounters(
_In_ swss::Table &countersTable)
{
SWSS_LOG_ENTER();
for (const auto &it : m_counterContext)
{
it.second->collectData(countersTable);
}
countersTable.flush();
}
void FlexCounter::runPlugins(
_In_ swss::DBConnector& counters_db)
{
SWSS_LOG_ENTER();
const std::vector<std::string> argv =
{
std::to_string(counters_db.getDbId()),
COUNTERS_TABLE,
std::to_string(m_pollInterval)
};
for (const auto &it : m_counterContext)
{
it.second->runPlugin(counters_db, argv);
}
}
void FlexCounter::flexCounterThreadRunFunction()
{
SWSS_LOG_ENTER();
swss::DBConnector db(m_dbCounters, 0);
swss::RedisPipeline pipeline(&db);
swss::Table countersTable(&pipeline, COUNTERS_TABLE, true);
while (m_runFlexCounterThread)
{
MUTEX;
if (m_enable && !allIdsEmpty() && (m_pollInterval > 0))
{
auto start = std::chrono::steady_clock::now();
collectCounters(countersTable);
runPlugins(db);
auto finish = std::chrono::steady_clock::now();
uint32_t delay = static_cast<uint32_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(finish - start).count());
uint32_t correction = delay % m_pollInterval;
correction = m_pollInterval - correction;
MUTEX_UNLOCK; // explicit unlock
SWSS_LOG_DEBUG("End of flex counter thread FC %s, took %d ms", m_instanceId.c_str(), delay);
std::unique_lock<std::mutex> lk(m_mtxSleep);
m_cvSleep.wait_for(lk, std::chrono::milliseconds(correction));
continue;
}
MUTEX_UNLOCK; // explicit unlock
// nothing to collect, wait until notified
waitPoll();
}
}
void FlexCounter::startFlexCounterThread()
{
SWSS_LOG_ENTER();
m_runFlexCounterThread = true;
m_flexCounterThread = std::make_shared<std::thread>(&FlexCounter::flexCounterThreadRunFunction, this);
SWSS_LOG_INFO("Flex Counter thread started");
}
void FlexCounter::endFlexCounterThread(void)
{
SWSS_LOG_ENTER();
MUTEX;
if (!m_runFlexCounterThread)
{
return;
}
m_runFlexCounterThread = false;
notifyPoll();
m_cvSleep.notify_all();
if (m_flexCounterThread != nullptr)
{
auto fcThread = std::move(m_flexCounterThread);
MUTEX_UNLOCK; // NOTE: explicit unlock before join to not cause deadlock
SWSS_LOG_INFO("Wait for Flex Counter thread to end");
fcThread->join();
}
SWSS_LOG_INFO("Flex Counter thread ended");
}
void FlexCounter::removeCounter(
_In_ sai_object_id_t vid)
{
MUTEX;
SWSS_LOG_ENTER();
auto objectType = VidManager::objectTypeQuery(vid);
if (objectType == SAI_OBJECT_TYPE_PORT)
{
if (hasCounterContext(COUNTER_TYPE_PORT))
{
getCounterContext(COUNTER_TYPE_PORT)->removeObject(vid);
}
if (hasCounterContext(COUNTER_TYPE_PORT_DEBUG))
{
getCounterContext(COUNTER_TYPE_PORT_DEBUG)->removeObject(vid);
}
if (hasCounterContext(COUNTER_TYPE_WRED_ECN_PORT))
{
getCounterContext(COUNTER_TYPE_WRED_ECN_PORT)->removeObject(vid);
}
}
else if (objectType == SAI_OBJECT_TYPE_QUEUE)
{
if (hasCounterContext(COUNTER_TYPE_QUEUE))
{
getCounterContext(COUNTER_TYPE_QUEUE)->removeObject(vid);
}
if (hasCounterContext(COUNTER_TYPE_WRED_ECN_QUEUE))
{
getCounterContext(COUNTER_TYPE_WRED_ECN_QUEUE)->removeObject(vid);
}
if (hasCounterContext(ATTR_TYPE_QUEUE))
{
getCounterContext(ATTR_TYPE_QUEUE)->removeObject(vid);
}
}
else if (objectType == SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP)
{
if (hasCounterContext(COUNTER_TYPE_PG))
{
getCounterContext(COUNTER_TYPE_PG)->removeObject(vid);
}
if (hasCounterContext(ATTR_TYPE_PG))
{
getCounterContext(ATTR_TYPE_PG)->removeObject(vid);
}
}
else if (objectType == SAI_OBJECT_TYPE_ROUTER_INTERFACE)
{
if (hasCounterContext(COUNTER_TYPE_RIF))
{
removeDataFromCountersDB(vid, ":RIF");
getCounterContext(COUNTER_TYPE_RIF)->removeObject(vid);
}
}
else if (objectType == SAI_OBJECT_TYPE_BUFFER_POOL)
{
if (hasCounterContext(COUNTER_TYPE_BUFFER_POOL))
{
getCounterContext(COUNTER_TYPE_BUFFER_POOL)->removeObject(vid);
}
}
else if (objectType == SAI_OBJECT_TYPE_SWITCH)
{
if (hasCounterContext(COUNTER_TYPE_SWITCH_DEBUG))
{
getCounterContext(COUNTER_TYPE_SWITCH_DEBUG)->removeObject(vid);
}
}
else if (objectType == SAI_OBJECT_TYPE_MACSEC_FLOW)
{
if (hasCounterContext(COUNTER_TYPE_MACSEC_FLOW))
{
getCounterContext(COUNTER_TYPE_MACSEC_FLOW)->removeObject(vid);
}
}
else if (objectType == SAI_OBJECT_TYPE_MACSEC_SA)
{
if (hasCounterContext(COUNTER_TYPE_MACSEC_SA))
{
getCounterContext(COUNTER_TYPE_MACSEC_SA)->removeObject(vid);
}
if (hasCounterContext(ATTR_TYPE_MACSEC_SA))
{
getCounterContext(ATTR_TYPE_MACSEC_SA)->removeObject(vid);
}
}
else if (objectType == SAI_OBJECT_TYPE_ACL_COUNTER)
{
if (hasCounterContext(ATTR_TYPE_ACL_COUNTER))
{
getCounterContext(ATTR_TYPE_ACL_COUNTER)->removeObject(vid);
}
}
else if (objectType == SAI_OBJECT_TYPE_TUNNEL)
{
if (hasCounterContext(COUNTER_TYPE_TUNNEL))
{
getCounterContext(COUNTER_TYPE_TUNNEL)->removeObject(vid);
}
}
else if (objectType == (sai_object_type_t)SAI_OBJECT_TYPE_ENI)
{
if (hasCounterContext(COUNTER_TYPE_ENI))
{
getCounterContext(COUNTER_TYPE_ENI)->removeObject(vid);
}
if (hasCounterContext(COUNTER_TYPE_METER_BUCKET))
{
getCounterContext(COUNTER_TYPE_METER_BUCKET)->removeObject(vid);
}
}
else if (objectType == SAI_OBJECT_TYPE_COUNTER)
{
if (hasCounterContext(COUNTER_TYPE_FLOW))
{
getCounterContext(COUNTER_TYPE_FLOW)->removeObject(vid);
removeDataFromCountersDB(vid, ":TRAP");
}
if (hasCounterContext(COUNTER_TYPE_SRV6))
{
getCounterContext(COUNTER_TYPE_SRV6)->removeObject(vid);
}
}
else if (objectType == SAI_OBJECT_TYPE_POLICER)
{
if (hasCounterContext(COUNTER_TYPE_POLICER))
{
getCounterContext(COUNTER_TYPE_POLICER)->removeObject(vid);
}
}
else
{
SWSS_LOG_ERROR("Object type for removal not supported, %s",
sai_serialize_object_type(objectType).c_str());
}
}
void FlexCounter::addCounter(
_In_ sai_object_id_t vid,
_In_ sai_object_id_t rid,
_In_ const std::vector<swss::FieldValueTuple>& values)
{
MUTEX;
SWSS_LOG_ENTER();
sai_object_type_t objectType = VidManager::objectTypeQuery(vid); // VID and RID will have the same object type
std::vector<std::string> counterIds;
std::string statsMode;
for (const auto& valuePair: values)
{
const auto field = fvField(valuePair);
const auto value = fvValue(valuePair);
auto idStrings = swss::tokenize(value, ',');
const auto &counterGroupRef = m_objectTypeField2CounterType.find({objectType, field});
if (counterGroupRef != m_objectTypeField2CounterType.end())
{
getCounterContext(counterGroupRef->second)->addObject(
vid,
rid,
idStrings,
"");
}
else if (objectType == SAI_OBJECT_TYPE_POLICER && field == POLICER_COUNTER_ID_LIST)
{
getCounterContext(COUNTER_TYPE_POLICER)->addObject(
vid,
rid,
idStrings,
"");
}
else if (objectType == SAI_OBJECT_TYPE_BUFFER_POOL && field == BUFFER_POOL_COUNTER_ID_LIST)
{
counterIds = idStrings;
}
else if (objectType == SAI_OBJECT_TYPE_BUFFER_POOL && field == STATS_MODE_FIELD)
{
statsMode = value;
}
else
{
SWSS_LOG_ERROR("Object type and field combination is not supported, object type %s, field %s",
sai_serialize_object_type(objectType).c_str(),
field.c_str());
}
}
// outside loop since required 2 fields BUFFER_POOL_COUNTER_ID_LIST and STATS_MODE_FIELD
if (objectType == SAI_OBJECT_TYPE_BUFFER_POOL && counterIds.size())
{
getCounterContext(COUNTER_TYPE_BUFFER_POOL)->addObject(
vid,
rid,
counterIds,
statsMode);
}
// notify thread to start polling
notifyPoll();
}
void FlexCounter::bulkAddCounter(
_In_ sai_object_type_t objectType,
_In_ const std::vector<sai_object_id_t>& vids,
_In_ const std::vector<sai_object_id_t>& rids,
_In_ const std::vector<swss::FieldValueTuple>& values)
{
MUTEX;
SWSS_LOG_ENTER();
std::vector<std::string> counterIds;
std::string statsMode;
for (const auto& valuePair: values)
{
const auto field = fvField(valuePair);
const auto value = fvValue(valuePair);
auto idStrings = swss::tokenize(value, ',');
const auto &counterGroupRef = m_objectTypeField2CounterType.find({objectType, field});
if (counterGroupRef != m_objectTypeField2CounterType.end())
{
getCounterContext(counterGroupRef->second)->bulkAddObject(
vids,
rids,
idStrings,
"");
}
else if (objectType == SAI_OBJECT_TYPE_BUFFER_POOL && field == BUFFER_POOL_COUNTER_ID_LIST)
{
counterIds = idStrings;
}
else if (objectType == SAI_OBJECT_TYPE_BUFFER_POOL && field == STATS_MODE_FIELD)
{
statsMode = value;
}
else
{
SWSS_LOG_ERROR("Object type and field combination is not supported, object type %s, field %s",
sai_serialize_object_type(objectType).c_str(),
field.c_str());
}
}
// outside loop since required 2 fields BUFFER_POOL_COUNTER_ID_LIST and STATS_MODE_FIELD
if (objectType == SAI_OBJECT_TYPE_BUFFER_POOL && counterIds.size())
{
getCounterContext(COUNTER_TYPE_BUFFER_POOL)->bulkAddObject(
vids,
rids,
counterIds,
statsMode);
}
// notify thread to start polling
notifyPoll();
}
void FlexCounter::waitPoll()
{
SWSS_LOG_ENTER();
std::unique_lock<std::mutex> lk(m_mtxSleep);
m_pollCond.wait(lk, [&](){return m_readyToPoll;});
m_readyToPoll = false;
}
void FlexCounter::notifyPoll()
{
SWSS_LOG_ENTER();
std::unique_lock<std::mutex> lk(m_mtxSleep);
m_readyToPoll = true;
m_pollCond.notify_all();
}