diff --git a/orchagent/flexcounterorch.cpp b/orchagent/flexcounterorch.cpp index 2832f0bd12..f6b7600a03 100644 --- a/orchagent/flexcounterorch.cpp +++ b/orchagent/flexcounterorch.cpp @@ -41,6 +41,8 @@ extern sai_object_id_t gSwitchId; #define FLOW_CNT_TRAP_KEY "FLOW_CNT_TRAP" #define FLOW_CNT_ROUTE_KEY "FLOW_CNT_ROUTE" #define ENI_KEY "ENI" +#define WRED_QUEUE_KEY "WRED_ECN_QUEUE" +#define WRED_PORT_KEY "WRED_ECN_PORT" unordered_map flexCounterGroupMap = { @@ -63,7 +65,9 @@ unordered_map flexCounterGroupMap = {"MACSEC_SA", COUNTERS_MACSEC_SA_GROUP}, {"MACSEC_SA_ATTR", COUNTERS_MACSEC_SA_ATTR_GROUP}, {"MACSEC_FLOW", COUNTERS_MACSEC_FLOW_GROUP}, - {"ENI", ENI_STAT_COUNTER_FLEX_COUNTER_GROUP} + {"ENI", ENI_STAT_COUNTER_FLEX_COUNTER_GROUP}, + {"WRED_ECN_PORT", WRED_PORT_STAT_COUNTER_FLEX_COUNTER_GROUP}, + {"WRED_ECN_QUEUE", WRED_QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP}, }; @@ -187,6 +191,17 @@ void FlexCounterOrch::doTask(Consumer &consumer) m_pg_watermark_enabled = true; gPortsOrch->addPriorityGroupWatermarkFlexCounters(getPgConfigurations()); } + else if(key == WRED_PORT_KEY) + { + gPortsOrch->generateWredPortCounterMap(); + m_wred_port_counter_enabled = true; + } + else if(key == WRED_QUEUE_KEY) + { + gPortsOrch->generateQueueMap(getQueueConfigurations()); + m_wred_queue_counter_enabled = true; + gPortsOrch->addWredQueueFlexCounters(getQueueConfigurations()); + } } if(gIntfsOrch && (key == RIF_KEY) && (value == "enable")) { @@ -292,6 +307,16 @@ bool FlexCounterOrch::getPgWatermarkCountersState() const return m_pg_watermark_enabled; } +bool FlexCounterOrch::getWredQueueCountersState() const +{ + return m_wred_queue_counter_enabled; +} + +bool FlexCounterOrch::getWredPortCountersState() const +{ + return m_wred_port_counter_enabled; +} + bool FlexCounterOrch::bake() { /* diff --git a/orchagent/flexcounterorch.h b/orchagent/flexcounterorch.h index 4bc74dc3b8..ccd72d125b 100644 --- a/orchagent/flexcounterorch.h +++ b/orchagent/flexcounterorch.h @@ -52,6 +52,8 @@ class FlexCounterOrch: public Orch std::map getPgConfigurations(); bool getHostIfTrapCounterState() const {return m_hostif_trap_counter_enabled;} bool getRouteFlowCountersState() const {return m_route_flow_counter_enabled;} + bool getWredQueueCountersState() const; + bool getWredPortCountersState() const; bool bake() override; private: @@ -63,6 +65,8 @@ class FlexCounterOrch: public Orch bool m_pg_watermark_enabled = false; bool m_hostif_trap_counter_enabled = false; bool m_route_flow_counter_enabled = false; + bool m_wred_queue_counter_enabled = false; + bool m_wred_port_counter_enabled = false; Table m_flexCounterConfigTable; Table m_bufferQueueConfigTable; Table m_bufferPgConfigTable; diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index ac3524d006..7833870a58 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -302,6 +302,22 @@ static const vector ingressPriorityGroupDropS SAI_INGRESS_PRIORITY_GROUP_STAT_DROPPED_PACKETS }; +const vector wred_port_stat_ids = +{ + SAI_PORT_STAT_GREEN_WRED_DROPPED_PACKETS, + SAI_PORT_STAT_YELLOW_WRED_DROPPED_PACKETS, + SAI_PORT_STAT_RED_WRED_DROPPED_PACKETS, + SAI_PORT_STAT_WRED_DROPPED_PACKETS +}; + +static const vector wred_queue_stat_ids = +{ + SAI_QUEUE_STAT_WRED_ECN_MARKED_PACKETS, + SAI_QUEUE_STAT_WRED_ECN_MARKED_BYTES, + SAI_QUEUE_STAT_WRED_DROPPED_PACKETS, + SAI_QUEUE_STAT_WRED_DROPPED_BYTES +}; + static char* hostif_vlan_tag[] = { [SAI_HOSTIF_VLAN_TAG_STRIP] = "SAI_HOSTIF_VLAN_TAG_STRIP", [SAI_HOSTIF_VLAN_TAG_KEEP] = "SAI_HOSTIF_VLAN_TAG_KEEP", @@ -535,6 +551,8 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vector(new DBConnector("STATE_DB", 0)); m_stateBufferMaximumValueTable = unique_ptr(new Table(m_state_db.get(), STATE_BUFFER_MAXIMUM_VALUE_TABLE)); + /* Initialize counter capability table*/ + m_queueCounterCapabilitiesTable = unique_ptr
(new Table(m_state_db.get(), STATE_QUEUE_COUNTER_CAPABILITIES_NAME)); + m_portCounterCapabilitiesTable = unique_ptr
(new Table(m_state_db.get(), STATE_PORT_COUNTER_CAPABILITIES_NAME)); + initGearbox(); string queueWmSha, pgWmSha, portRateSha; @@ -807,6 +829,9 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vector qstat_cap_list(queue_stat_count, stat_initializer); + queue_stats_capability.count = queue_stat_count; + queue_stats_capability.list = qstat_cap_list.data(); + + vector fieldValuesTrue; + fieldValuesTrue.push_back(FieldValueTuple("isSupported", "true")); + + vector fieldValuesFalse; + fieldValuesFalse.push_back(FieldValueTuple("isSupported", "false")); + + /* 1. Initialize the WRED stats capabilities with false for all counters */ + m_queueCounterCapabilitiesTable->set("WRED_ECN_QUEUE_ECN_MARKED_PKT_COUNTER",fieldValuesFalse); + m_queueCounterCapabilitiesTable->set("WRED_ECN_QUEUE_ECN_MARKED_BYTE_COUNTER",fieldValuesFalse); + m_queueCounterCapabilitiesTable->set("WRED_ECN_QUEUE_WRED_DROPPED_PKT_COUNTER",fieldValuesFalse); + m_queueCounterCapabilitiesTable->set("WRED_ECN_QUEUE_WRED_DROPPED_BYTE_COUNTER",fieldValuesFalse); + m_portCounterCapabilitiesTable->set("WRED_ECN_PORT_WRED_GREEN_DROP_COUNTER",fieldValuesFalse); + m_portCounterCapabilitiesTable->set("WRED_ECN_PORT_WRED_YELLOW_DROP_COUNTER",fieldValuesFalse); + m_portCounterCapabilitiesTable->set("WRED_ECN_PORT_WRED_RED_DROP_COUNTER",fieldValuesFalse); + m_portCounterCapabilitiesTable->set("WRED_ECN_PORT_WRED_TOTAL_DROP_COUNTER",fieldValuesFalse); + + /* 2. Get queue stats capability from the platform */ + sai_status_t status = sai_query_stats_capability(switchId, SAI_OBJECT_TYPE_QUEUE, &queue_stats_capability); + if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + qstat_cap_list.resize(queue_stats_capability.count); + queue_stats_capability.list = qstat_cap_list.data(); + status = sai_query_stats_capability(switchId, SAI_OBJECT_TYPE_QUEUE, &queue_stats_capability); + } + if (status == SAI_STATUS_SUCCESS) + { + /* 3. Based on the fetched queue stats capability, update the STATE_DB */ + for(it=0; itset("WRED_ECN_QUEUE_ECN_MARKED_PKT_COUNTER",fieldValuesTrue); + q_ecn_pkt = true; + } + else if (SAI_QUEUE_STAT_WRED_ECN_MARKED_BYTES == queue_stats_capability.list[it].stat_enum) + { + m_queueCounterCapabilitiesTable->set("WRED_ECN_QUEUE_ECN_MARKED_BYTE_COUNTER",fieldValuesTrue); + q_ecn_byte = true; + } + else if (SAI_QUEUE_STAT_WRED_DROPPED_PACKETS == queue_stats_capability.list[it].stat_enum) + { + m_queueCounterCapabilitiesTable->set("WRED_ECN_QUEUE_WRED_DROPPED_PKT_COUNTER",fieldValuesTrue); + q_wred_pkt = true; + } + else if (SAI_QUEUE_STAT_WRED_DROPPED_BYTES == queue_stats_capability.list[it].stat_enum) + { + m_queueCounterCapabilitiesTable->set("WRED_ECN_QUEUE_WRED_DROPPED_BYTE_COUNTER",fieldValuesTrue); + q_wred_byte = true; + } + + } + SWSS_LOG_INFO("WRED queue stats is_capable: [ecn-marked-pkts:%d,ecn-marked-bytes:%d,wred-drop-pkts:%d,wred-drop-bytes:%d]", + q_ecn_pkt, q_ecn_byte, q_wred_pkt, q_wred_byte); + } + else + { + SWSS_LOG_NOTICE("Queue stat capability get failed: WRED queue stats can not be enabled, rv:%d", status); + } + + vector pstat_cap_list(port_stat_count, stat_initializer); + port_stats_capability.count = port_stat_count; + port_stats_capability.list = pstat_cap_list.data(); + + /* 4. Get port stats capability from the platform*/ + status = sai_query_stats_capability(switchId, SAI_OBJECT_TYPE_PORT, &port_stats_capability); + if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + pstat_cap_list.resize(port_stats_capability.count); + port_stats_capability.list = pstat_cap_list.data(); + status = sai_query_stats_capability(switchId, SAI_OBJECT_TYPE_PORT, &port_stats_capability); + } + if (status == SAI_STATUS_SUCCESS) + { + /* 5. Based on the fetched port stats capability, update the STATE_DB*/ + for(it=0; itset("WRED_ECN_PORT_WRED_GREEN_DROP_COUNTER",fieldValuesTrue); + pt_grn_pkt = true; + } + else if (SAI_PORT_STAT_YELLOW_WRED_DROPPED_PACKETS == port_stats_capability.list[it].stat_enum) + { + m_portCounterCapabilitiesTable->set("WRED_ECN_PORT_WRED_YELLOW_DROP_COUNTER",fieldValuesTrue); + pt_ylw_pkt = true; + } + else if (SAI_PORT_STAT_RED_WRED_DROPPED_PACKETS == port_stats_capability.list[it].stat_enum) + { + m_portCounterCapabilitiesTable->set("WRED_ECN_PORT_WRED_RED_DROP_COUNTER",fieldValuesTrue); + pt_red_pkt = true; + } + else if (SAI_PORT_STAT_WRED_DROPPED_PACKETS == port_stats_capability.list[it].stat_enum) + { + m_portCounterCapabilitiesTable->set("WRED_ECN_PORT_WRED_TOTAL_DROP_COUNTER",fieldValuesTrue); + pt_tot_pkt = true; + } + } + SWSS_LOG_INFO("WRED port drop stats is_capable: [wred-grn-pkts:%d,wred-ylw-pkts:%d,wred-red-pkts:%d,wred-total-pkts:%d]", + pt_grn_pkt, pt_ylw_pkt, pt_red_pkt, pt_tot_pkt); + } + else + { + SWSS_LOG_NOTICE("Port stat capability get failed: WRED port stats can not be enabled, rv:%d", status); + } +} + bool PortsOrch::getPortByBridgePortId(sai_object_id_t bridge_port_id, Port &port) { SWSS_LOG_ENTER(); @@ -3491,6 +3656,16 @@ string PortsOrch::getPriorityGroupDropPacketsFlexCounterTableKey(string key) { return string(PG_DROP_STAT_COUNTER_FLEX_COUNTER_GROUP) + ":" + key; } +/**** +* Func Name : getWredQueueFlexCounterTableKey +* Parameters : Key as string +* Returns : Returns the Wred queue stat flexcounter table Key +* Description: Form the key and return +**/ +string PortsOrch::getWredQueueFlexCounterTableKey(string key) +{ + return string(WRED_QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP) + ":" + key; +} bool PortsOrch::initPort(const PortConfig &port) { @@ -3559,6 +3734,12 @@ bool PortsOrch::initPort(const PortConfig &port) port_buffer_drop_stat_manager.setCounterIdList(p.m_port_id, CounterType::PORT, port_buffer_drop_stats); } + if (flex_counters_orch->getWredPortCountersState()) + { + auto wred_port_stats = generateCounterStats(WRED_PORT_STAT_COUNTER_FLEX_COUNTER_GROUP); + wred_port_stat_manager.setCounterIdList(p.m_port_id, CounterType::PORT, wred_port_stats); + } + PortUpdate update = { p, true }; notify(SUBJECT_TYPE_PORT_CHANGE, static_cast(&update)); @@ -3621,6 +3802,10 @@ void PortsOrch::deInitPort(string alias, sai_object_id_t port_id) { port_buffer_drop_stat_manager.clearCounterIdList(p.m_port_id); } + if (flex_counters_orch->getWredPortCountersState()) + { + wred_port_stat_manager.clearCounterIdList(p.m_port_id); + } /* remove port name map from counter table */ m_counterTable->hdel("", alias); @@ -7653,6 +7838,12 @@ void PortsOrch::createPortBufferQueueCounters(const Port &port, string queues, b /* add watermark queue counters */ addQueueWatermarkFlexCountersPerPortPerQueueIndex(port, queueIndex); } + + if (flexCounterOrch->getWredQueueCountersState()) + { + /* add wred queue counters */ + addWredQueueFlexCountersPerPortPerQueueIndex(port, queueIndex, false); + } } m_queueTable->set("", queueVector); @@ -7713,6 +7904,11 @@ void PortsOrch::removePortBufferQueueCounters(const Port &port, string queues, b string key = getQueueWatermarkFlexCounterTableKey(id); stopFlexCounterPolling(gSwitchId, key); } + if (flexCounterOrch->getWredQueueCountersState()) + { + /* Remove wred queue counters */ + wred_queue_stat_manager.clearCounterIdList(port.m_queue_ids[queueIndex]); + } } CounterCheckOrch::getInstance().removePort(port); @@ -8066,6 +8262,115 @@ void PortsOrch::generatePortBufferDropCounterMap() m_isPortBufferDropCounterMapGenerated = true; } +/**** +* Func Name : generateWredPortCounterMap +* Parameters : None +* Returns : void +* Description: Set the list of counters to be used for syncd counter polling +**/ +void PortsOrch::generateWredPortCounterMap() +{ + if (m_isWredPortCounterMapGenerated) + { + return; + } + + auto wred_port_stats = generateCounterStats(WRED_PORT_STAT_COUNTER_FLEX_COUNTER_GROUP); + for (const auto& it: m_portList) + { + // Set counter stats only for PHY ports to ensure syncd will not try to query the counter statistics from the HW for non-PHY ports. + if (it.second.m_type != Port::Type::PHY) + { + continue; + } + wred_port_stat_manager.setCounterIdList(it.second.m_port_id, CounterType::PORT, wred_port_stats); + } + + m_isWredPortCounterMapGenerated = true; +} + +/**** +* Func Name : addWredQueueFlexCounters +* Parameters : queueStateVector +* Returns : void +* Description: Top level API to Set WRED flex counters for Queues +**/ +void PortsOrch::addWredQueueFlexCounters(map queuesStateVector) +{ + if (m_isWredQueueCounterMapGenerated) + { + return; + } + + for (const auto& it: m_portList) + { + if (it.second.m_type == Port::PHY) + { + if (!queuesStateVector.count(it.second.m_alias)) + { + auto maxQueueNumber = getNumberOfPortSupportedQueueCounters(it.second.m_alias); + FlexCounterQueueStates flexCounterQueueState(maxQueueNumber); + queuesStateVector.insert(make_pair(it.second.m_alias, flexCounterQueueState)); + } + addWredQueueFlexCountersPerPort(it.second, queuesStateVector.at(it.second.m_alias)); + } + } + + m_isWredQueueCounterMapGenerated = true; +} + +/**** +* Func Name : addWredQueueFlexCountersPerPort +* Parameters : port and Queuestate +* Returns : void +* Description: Port level API to program flexcounter for queues +**/ +void PortsOrch::addWredQueueFlexCountersPerPort(const Port& port, FlexCounterQueueStates& queuesState) +{ + /* Add stat counters to flex_counter */ + + for (size_t queueIndex = 0; queueIndex < port.m_queue_ids.size(); ++queueIndex) + { + string queueType; + uint8_t queueRealIndex = 0; + if (getQueueTypeAndIndex(port.m_queue_ids[queueIndex], queueType, queueRealIndex)) + { + if (!queuesState.isQueueCounterEnabled(queueRealIndex)) + { + continue; + } + addWredQueueFlexCountersPerPortPerQueueIndex(port, queueIndex, false); + } + } +} +/**** +* Func Name : addWredQueueFlexCountersPerPortPerQueueIndex +* Parameters : port, queueIndex, is_voq +* Returns : void +* Description: Sets the Stats list to be polled by the flexcounter +**/ + +void PortsOrch::addWredQueueFlexCountersPerPortPerQueueIndex(const Port& port, size_t queueIndex, bool voq) +{ + std::unordered_set counter_stats; + std::vector queue_ids; + + for (const auto& it: wred_queue_stat_ids) + { + counter_stats.emplace(sai_serialize_queue_stat(it)); + } + if (voq) + { + queue_ids = m_port_voq_ids[port.m_alias]; + } + else + { + queue_ids = port.m_queue_ids; + } + + wred_queue_stat_manager.setCounterIdList(queue_ids[queueIndex], CounterType::QUEUE, counter_stats); +} + uint32_t PortsOrch::getNumberOfPortSupportedPgCounters(string port) { return static_cast(m_portList[port].m_priority_group_ids.size()); @@ -9514,6 +9819,13 @@ std::unordered_set PortsOrch::generateCounterStats(const string& ty counter_stats.emplace(sai_serialize_port_stat(it)); } } + else if (type == WRED_PORT_STAT_COUNTER_FLEX_COUNTER_GROUP) + { + for (const auto& it: wred_port_stat_ids) + { + counter_stats.emplace(sai_serialize_port_stat(it)); + } + } return counter_stats; } diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 4d01626cb9..3d5eca0a81 100644 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -35,6 +35,8 @@ #define PG_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS "60000" #define PG_DROP_FLEX_STAT_COUNTER_POLL_MSECS "10000" #define PORT_RATE_FLEX_COUNTER_POLLING_INTERVAL_MS "1000" +#define WRED_QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP "WRED_ECN_QUEUE_STAT_COUNTER" +#define WRED_PORT_STAT_COUNTER_FLEX_COUNTER_GROUP "WRED_ECN_PORT_STAT_COUNTER" typedef std::vector PortSupportedSpeeds; typedef std::set PortSupportedFecModes; @@ -190,6 +192,7 @@ class PortsOrch : public Orch, public Subject void removePortBufferQueueCounters(const Port &port, string queues, bool skip_host_tx_queue=true); void addQueueFlexCounters(map queuesStateVector); void addQueueWatermarkFlexCounters(map queuesStateVector); + void addWredQueueFlexCounters(map queuesStateVector); void generatePriorityGroupMap(map pgsStateVector); uint32_t getNumberOfPortSupportedPgCounters(string port); @@ -201,6 +204,9 @@ class PortsOrch : public Orch, public Subject void generatePortCounterMap(); void generatePortBufferDropCounterMap(); + void generateWredPortCounterMap(); + void generateWredQueueCounterMap(); + void refreshPortStatus(); bool removeAclTableGroup(const Port &p); @@ -271,6 +277,7 @@ class PortsOrch : public Orch, public Subject std::string getPriorityGroupWatermarkFlexCounterTableKey(std::string s); std::string getPriorityGroupDropPacketsFlexCounterTableKey(std::string s); std::string getPortRateFlexCounterTableKey(std::string s); + std::string getWredQueueFlexCounterTableKey(std::string s); shared_ptr m_counter_db; shared_ptr m_state_db; @@ -279,6 +286,8 @@ class PortsOrch : public Orch, public Subject FlexCounterManager port_stat_manager; FlexCounterManager port_buffer_drop_stat_manager; FlexCounterManager queue_stat_manager; + FlexCounterManager wred_port_stat_manager; + FlexCounterManager wred_queue_stat_manager; FlexCounterManager gb_port_stat_manager; shared_ptr m_gb_counter_db; @@ -447,6 +456,10 @@ class PortsOrch : public Orch, public Subject void addQueueWatermarkFlexCountersPerPort(const Port& port, FlexCounterQueueStates& queuesState); void addQueueWatermarkFlexCountersPerPortPerQueueIndex(const Port& port, size_t queueIndex); + bool m_isWredQueueCounterMapGenerated = false; + void addWredQueueFlexCountersPerPort(const Port& port, FlexCounterQueueStates& queuesState); + void addWredQueueFlexCountersPerPortPerQueueIndex(const Port& port, size_t queueIndex, bool voq); + bool m_isPriorityGroupMapGenerated = false; void generatePriorityGroupMapPerPort(const Port& port, FlexCounterPgStates& pgsState); bool m_isPriorityGroupFlexCountersAdded = false; @@ -553,6 +566,10 @@ class PortsOrch : public Orch, public Subject map m_portPtTam; // Define whether the switch supports or not Path Tracing bool m_isPathTracingSupported = false; + void initCounterCapabilities(sai_object_id_t switchId); + bool m_isWredPortCounterMapGenerated = false; + std::unique_ptr m_queueCounterCapabilitiesTable = nullptr; + std::unique_ptr m_portCounterCapabilitiesTable = nullptr; private: // Port config aggregator diff --git a/tests/test_flex_counters.py b/tests/test_flex_counters.py index 1963248c2f..40fbaad3bb 100644 --- a/tests/test_flex_counters.py +++ b/tests/test_flex_counters.py @@ -78,6 +78,16 @@ 'name_map': 'COUNTERS_ROUTE_NAME_MAP', 'pre_test': 'pre_route_flow_counter_test', 'post_test': 'post_route_flow_counter_test', + }, + 'wred_queue_counter': { + 'key': 'WRED_ECN_QUEUE', + 'group_name': 'WRED_ECN_QUEUE_STAT_COUNTER', + 'name_map': 'COUNTERS_QUEUE_NAME_MAP', + }, + 'wred_port_counter': { + 'key': 'WRED_ECN_PORT', + 'group_name': 'WRED_ECN_PORT_STAT_COUNTER', + 'name_map': 'COUNTERS_PORT_NAME_MAP', } } @@ -609,7 +619,7 @@ def remove_ip_address(self, interface, ip): def set_admin_status(self, interface, status): self.config_db.update_entry("PORT", interface, {"admin_status": status}) - @pytest.mark.parametrize('counter_type_id', [('queue_counter', '8'), ('pg_drop_counter', '7')]) + @pytest.mark.parametrize('counter_type_id', [('queue_counter', '8'), ('pg_drop_counter', '7'), ('wred_queue_counter', '6')]) def test_create_only_config_db_buffers_false(self, dvs, counter_type_id): """ Test steps: @@ -659,7 +669,7 @@ def test_create_remove_buffer_pg_watermark_counter(self, dvs): self.wait_for_buffer_pg_queue_counter(meta_data['name_map'], 'Ethernet0', '1', False) self.wait_for_id_list_remove(meta_data['group_name'], "Ethernet0", counter_oid) - @pytest.mark.parametrize('counter_type_id', [('queue_counter', '8'), ('pg_drop_counter', '7')]) + @pytest.mark.parametrize('counter_type_id', [('queue_counter', '8'), ('pg_drop_counter', '7'), ('wred_queue_counter', '6')]) def test_create_only_config_db_buffers_true(self, dvs, counter_type_id): """ Test steps: