From 55ced7db0155d161d5637f889453e7d53cdbbf10 Mon Sep 17 00:00:00 2001 From: Dante Su Date: Fri, 29 Apr 2022 13:53:17 +0000 Subject: [PATCH] incorporate autoneg support from PR#2215 Signed-off-by: Dante Su --- orchagent/port.h | 1 + orchagent/portsorch.cpp | 123 ++++++++++++++++++++++++++++++++++++---- orchagent/portsorch.h | 4 ++ tests/test_port_an.py | 17 ++++++ 4 files changed, 135 insertions(+), 10 deletions(-) diff --git a/orchagent/port.h b/orchagent/port.h index 114b053f01..be56fe1704 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -180,6 +180,7 @@ class Port bool m_fec_cfg = false; bool m_an_cfg = false; + int m_cap_an = -1; /* Capability - AutoNeg, -1 means not set */ int m_cap_lt = -1; /* Capability - LinkTraining, -1 means not set */ }; diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 8d1bc48105..9530fc064c 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -61,6 +61,7 @@ extern string gMyAsicName; #define DEFAULT_VLAN_ID 1 #define MAX_VALID_VLAN_ID 4094 +#define PORT_SPEED_LIST_DEFAULT_SIZE 16 #define PORT_STATE_POLLING_SEC 5 #define PORT_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS 1000 #define PORT_BUFFER_DROP_STAT_POLLING_INTERVAL_MS 60000 @@ -1930,21 +1931,30 @@ void PortsOrch::initPortSupportedSpeeds(const std::string& alias, sai_object_id_ m_portStateTable.set(alias, v); } +void PortsOrch::initPortCapAutoNeg(Port &port) +{ + sai_status_t status; + sai_attribute_t attr; + + attr.id = SAI_PORT_ATTR_SUPPORTED_AUTO_NEG_MODE; + status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); + if (status == SAI_STATUS_SUCCESS) + { + port.m_cap_an = attr.value.booldata ? 1 : 0; + } + else + { + port.m_cap_an = 0; + SWSS_LOG_NOTICE("Unable to get %s AN capability", port.m_alias.c_str()); + } +} + void PortsOrch::initPortCapLinkTraining(Port &port) { sai_status_t status; sai_attribute_t attr; -#ifdef SAI_PORT_ATTR_SUPPORTED_LINK_TRAINING_MODE attr.id = SAI_PORT_ATTR_SUPPORTED_LINK_TRAINING_MODE; -#else - /* - * Fallback to autoneg upon legacy SAI implementation - * In the case of SFP/QSFP ports, link-training is most likely available - * if autoneg is supported. - */ - attr.id = SAI_PORT_ATTR_SUPPORTED_AUTO_NEG_MODE; -#endif status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); if (status == SAI_STATUS_SUCCESS) { @@ -1952,7 +1962,7 @@ void PortsOrch::initPortCapLinkTraining(Port &port) } else { - port.m_cap_lt = 1; /* Default to 1 for vstest */ + port.m_cap_lt = 0; SWSS_LOG_NOTICE("Unable to get %s LT capability", port.m_alias.c_str()); } } @@ -2111,6 +2121,45 @@ bool PortsOrch::getPortSpeed(sai_object_id_t id, sai_uint32_t &speed) return true; } +bool PortsOrch::getPortAdvSpeeds(const Port& port, bool remote, std::vector& speed_list) +{ + sai_object_id_t port_id = port.m_port_id; + sai_object_id_t line_port_id; + sai_attribute_t attr; + sai_status_t status; + std::vector speeds(PORT_SPEED_LIST_DEFAULT_SIZE); + + attr.id = remote ? SAI_PORT_ATTR_REMOTE_ADVERTISED_SPEED : SAI_PORT_ATTR_ADVERTISED_SPEED; + attr.value.u32list.count = static_cast(speeds.size()); + attr.value.u32list.list = speeds.data(); + + if (getDestPortId(port_id, LINE_PORT_TYPE, line_port_id)) + { + status = sai_port_api->get_port_attribute(line_port_id, 1, &attr); + } + else + { + status = sai_port_api->get_port_attribute(port_id, 1, &attr); + } + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Unable to get advertised speed for %s", port.m_alias.c_str()); + return false; + } + speeds.resize(attr.value.u32list.count); + speed_list.swap(speeds); + return true; +} + +bool PortsOrch::getPortAdvSpeeds(const Port& port, bool remote, string& adv_speeds) +{ + std::vector speed_list; + bool rc = getPortAdvSpeeds(port, remote, speed_list); + + adv_speeds = rc ? swss::join(',', speed_list.begin(), speed_list.end()) : ""; + return rc; +} + task_process_status PortsOrch::setPortAdvSpeeds(sai_object_id_t port_id, std::vector& speed_list) { SWSS_LOG_ENTER(); @@ -3015,6 +3064,18 @@ void PortsOrch::doPortTask(Consumer &consumer) { if (!an_str.empty()) { + if (p.m_cap_an < 0) + { + initPortCapAutoNeg(p); + m_portList[alias] = p; + } + if (p.m_cap_an < 1) + { + SWSS_LOG_ERROR("%s: autoneg is not supported", p.m_alias.c_str()); + // Invalid auto negotiation mode configured, don't retry + it = consumer.m_toSync.erase(it); + continue; + } if (autoneg_mode_map.find(an_str) == autoneg_mode_map.end()) { SWSS_LOG_ERROR("Failed to parse autoneg value: %s", an_str.c_str()); @@ -3057,6 +3118,8 @@ void PortsOrch::doPortTask(Consumer &consumer) SWSS_LOG_NOTICE("Set port %s AutoNeg from %d to %d", alias.c_str(), p.m_autoneg, an); p.m_autoneg = an; m_portList[alias] = p; + m_portStateTable.hdel(p.m_alias, "rmt_adv_speeds"); + updatePortStatePoll(p, PORT_STATE_POLL_AN, (an > 0)); } } @@ -5949,6 +6012,20 @@ void PortsOrch::updatePortOperStatus(Port &port, sai_port_oper_status_t status) if (port.m_type == Port::PHY) { updateDbPortOperStatus(port, status); + if (port.m_admin_state_up && port.m_autoneg > 0) + { + if (status == SAI_PORT_OPER_STATUS_UP) + { + updatePortStateAutoNeg(port); + updatePortStatePoll(port, PORT_STATE_POLL_AN, false); + } + else + { + /* Restart autoneg state polling upon link down event */ + m_portStateTable.hdel(port.m_alias, "rmt_adv_speeds"); + updatePortStatePoll(port, PORT_STATE_POLL_AN, true); + } + } if (port.m_admin_state_up && port.m_link_training > 0) { updatePortStateLinkTraining(port); @@ -7147,6 +7224,28 @@ bool PortsOrch::decrFdbCount(const std::string& alias, int count) return true; } +void PortsOrch::updatePortStateAutoNeg(const Port &port) +{ + SWSS_LOG_ENTER(); + + if (port.m_type != Port::Type::PHY) + { + return; + } + + string adv_speeds; + + if (getPortAdvSpeeds(port, true, adv_speeds)) + { + m_portStateTable.hset(port.m_alias, "rmt_adv_speeds", adv_speeds); + } + else + { + m_portStateTable.hset(port.m_alias, "rmt_adv_speeds", "N/A"); + updatePortStatePoll(port, PORT_STATE_POLL_AN, false); + } +} + void PortsOrch::updatePortStateLinkTraining(const Port &port) { SWSS_LOG_ENTER(); @@ -7221,6 +7320,10 @@ void PortsOrch::doTask(swss::SelectableTimer &timer) ++it; continue; } + if (it->second & PORT_STATE_POLL_AN) + { + updatePortStateAutoNeg(port); + } if (it->second & PORT_STATE_POLL_LT) { updatePortStateLinkTraining(port); diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index c3bd83ed38..8814225e1c 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -293,6 +293,7 @@ class PortsOrch : public Orch, public Subject bool initPort(const string &alias, const string &role, const int index, const set &lane_set); void deInitPort(string alias, sai_object_id_t port_id); + void initPortCapAutoNeg(Port &port); void initPortCapLinkTraining(Port &port); bool setPortAdminStatus(Port &port, bool up); @@ -315,6 +316,8 @@ class PortsOrch : public Orch, public Subject bool setGearboxPortsAttr(Port &port, sai_port_attr_t id, void *value); bool setGearboxPortAttr(Port &port, dest_port_type_t port_type, sai_port_attr_t id, void *value); + bool getPortAdvSpeeds(const Port& port, bool remote, std::vector& speed_list); + bool getPortAdvSpeeds(const Port& port, bool remote, string& adv_speeds); task_process_status setPortAdvSpeeds(sai_object_id_t port_id, std::vector& speed_list); bool getQueueTypeAndIndex(sai_object_id_t queue_id, string &type, uint8_t &index); @@ -352,6 +355,7 @@ class PortsOrch : public Orch, public Subject map m_port_state_poll; void updatePortStatePoll(const Port &port, port_state_poll_t type, bool active); + void updatePortStateAutoNeg(const Port &port); void updatePortStateLinkTraining(const Port &port); void getPortSerdesVal(const std::string& s, std::vector &lane_values); diff --git a/tests/test_port_an.py b/tests/test_port_an.py index dc98f43d0e..9c004aa790 100644 --- a/tests/test_port_an.py +++ b/tests/test_port_an.py @@ -293,6 +293,23 @@ def test_PortAutoNegWarm(self, dvs, testlog): # slow down crm polling dvs.crm_poll_set("10000") + def test_PortAutoNegRemoteAdvSpeeds(self, dvs, testlog): + + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + sdb = swsscommon.DBConnector(6, dvs.redis_sock, 0) + + ctbl = swsscommon.Table(cdb, "PORT") + stbl = swsscommon.Table(sdb, "PORT_TABLE") + + # set autoneg = true and admin_status = up + fvs = swsscommon.FieldValuePairs([("autoneg","on"),("admin_status","up")]) + ctbl.set("Ethernet0", fvs) + + time.sleep(10) + + (status, fvs) = stbl.get("Ethernet0") + assert status == True + assert "rmt_adv_speeds" in [fv[0] for fv in fvs] # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down before retrying