From 3161eaae6bd589bf8c9819aefb9d738047e0c9f8 Mon Sep 17 00:00:00 2001 From: "Dante (Kuo-Jung) Su" Date: Fri, 29 Jul 2022 01:08:18 +0800 Subject: [PATCH] portsorch: initial support for link-training (#2359) * Squashed commit of the following: commit 18632a3e84de380ad29a671a6496018cfd1cd540 Author: Dante Su Date: Mon May 23 12:22:49 2022 +0000 optimize port state refresh logic Signed-off-by: Dante Su commit 081d491c9bc3cda80343d1476b76c457bdeacb8e Author: ds952811 Date: Mon May 23 02:33:56 2022 +0000 address review comments Signed-off-by: ds952811 commit 84bdde4913cae37dc96ceb110cb2503633922847 Author: Dante Su Date: Fri May 20 02:15:59 2022 +0000 update the default LT capability upon get failures Signed-off-by: Dante Su commit 0f73666ba3b13af2116e55b0238e96c6e9c49270 Author: Dante Su Date: Thu May 19 11:28:38 2022 +0000 Rename updatePortStatesXX as refreshPortStatesXX Signed-off-by: Dante Su commit ddd57fe08f78f4463ee39a2075b0b0b0d56e9117 Author: Dante Su Date: Thu May 19 04:03:13 2022 +0000 Have AN cap defaults to 1, and use AN attr for LT cap query Signed-off-by: Dante Su commit 876e605b8462c4318f09af86767453907d055e5b Author: Dante Su Date: Fri May 13 11:15:12 2022 +0000 drop LT capability query Signed-off-by: Dante Su commit 55ced7db0155d161d5637f889453e7d53cdbbf10 Author: Dante Su Date: Fri Apr 29 13:53:17 2022 +0000 incorporate autoneg support from PR#2215 Signed-off-by: Dante Su commit a04594e6efee7d5f5e3b86f45c413196affc89a8 Author: Dante Su Date: Thu Apr 28 16:33:14 2022 +0000 address review comments Signed-off-by: Dante Su commit e9eeb9a87f27740ee692e0a1e86e4a10dd9e943f Author: Dante Su Date: Thu Apr 28 15:00:04 2022 +0000 address review comments Signed-off-by: Dante Su commit 4ff604da578b3fa2a40544c6f800c68cef4b9622 Author: Dante Su Date: Fri Apr 22 03:51:56 2022 +0000 Stop the port state poll by default Signed-off-by: Dante Su commit bdfb8d847fc81bc4771592d18e8e0747114688b3 Author: Dante Su Date: Fri Apr 22 03:48:07 2022 +0000 address review comments Signed-off-by: Dante Su commit 1c6bda8279bcdcce564bdf83c59dc6a3ac7e3f97 Author: Dante Su Date: Mon Apr 18 08:46:21 2022 +0000 Restore pre-emphasis when LT is transitioned from ON to OFF Signed-off-by: Dante Su commit 09a9b334f8f4b06d399e4b3af73443f4b16d5640 Author: Dante Su Date: Mon Apr 18 02:33:11 2022 +0000 fix build failure due to SAI_PORT_ATTR_SUPPORTED_LINK_TRAINING_MODE Signed-off-by: Dante Su commit b0bee3ec7bd1c00c976e812eae27f0e88d41f630 Author: Dante Su Date: Thu Apr 14 07:54:14 2022 +0000 address review comments Signed-off-by: Dante Su commit c4345efbafd1881ab6b80e878e2ecb9d7e637b3b Author: Dante Su Date: Fri Mar 25 02:26:05 2022 +0000 portsorch: initial support for link-training - What I did Add Link-Training support to portsorch, while Gearbox is not in the scope - Why I did it In the case of DAC, static pre-calibrated pre-emphasis is rarely available on SONIC, as most of the ODM are expecting this to be done dynamically at runtime via link-training, hence we'll need this feature to improve the link quality - How I verified it Manual test Ran the Unit-tests to the corresponding changes Signed-off-by: Dante Su * Add support for selected multiple tests Signed-off-by: Dante Su * Revert "Add support for selected multiple tests" This reverts commit 8e2f7a4334278589581b2110e76f4252bbec03f0. * fix the comment for 'autoneg is not supported' Signed-off-by: Dante Su * address review comments Signed-off-by: Dante Su * validate AN cap only when there is an update to AN config Signed-off-by: Dante Su * drop the changes to tests/conftest.py Signed-off-by: Dante Su * fix link failure in p4orch_tests-fake_portorch.o Signed-off-by: Dante Su --- orchagent/p4orch/tests/fake_portorch.cpp | 6 +- orchagent/port.h | 7 + orchagent/portsorch.cpp | 383 ++++++++++++++++++++++- orchagent/portsorch.h | 23 ++ tests/test_port_an.py | 17 + tests/test_port_lt.py | 139 ++++++++ 6 files changed, 569 insertions(+), 6 deletions(-) create mode 100644 tests/test_port_lt.py diff --git a/orchagent/p4orch/tests/fake_portorch.cpp b/orchagent/p4orch/tests/fake_portorch.cpp index 51ff4503127c..1cd3e20ae364 100644 --- a/orchagent/p4orch/tests/fake_portorch.cpp +++ b/orchagent/p4orch/tests/fake_portorch.cpp @@ -689,4 +689,8 @@ void PortsOrch::voqSyncDelLagMember(Port &lag, Port &port) std::unordered_set PortsOrch::generateCounterStats(const string &type, bool gearbox) { return {}; -} \ No newline at end of file +} + +void PortsOrch::doTask(swss::SelectableTimer &timer) +{ +} diff --git a/orchagent/port.h b/orchagent/port.h index e5ba8134f598..a5e003584b3b 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -119,6 +119,7 @@ class Port uint32_t m_speed = 0; // Mbps std::string m_learn_mode = "hardware"; AutoNegMode m_autoneg = Port::AutoNegMode::AUTONEG_NOT_SET; + int m_link_training = -1; // -1 means not set, 0 = disabled, 1 = enabled bool m_admin_state_up = false; bool m_init = false; bool m_l3_vni = false; @@ -177,8 +178,14 @@ class Port sai_object_id_t m_system_side_id = 0; sai_object_id_t m_line_side_id = 0; + /* pre-emphasis */ + std::map> m_preemphasis; + 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 e9a3afdc1e80..3a0b4e75f054 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -61,6 +61,8 @@ 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 #define QUEUE_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS 10000 @@ -112,6 +114,26 @@ static map autoneg_mode_map = { "off", 0 } }; +static map link_training_mode_map = +{ + { "on", 1 }, + { "off", 0 } +}; + +static map link_training_failure_map = +{ + { SAI_PORT_LINK_TRAINING_FAILURE_STATUS_NO_ERROR, "none" }, + { SAI_PORT_LINK_TRAINING_FAILURE_STATUS_FRAME_LOCK_ERROR, "frame_lock"}, + { SAI_PORT_LINK_TRAINING_FAILURE_STATUS_SNR_LOWER_THRESHOLD, "snr_low"}, + { SAI_PORT_LINK_TRAINING_FAILURE_STATUS_TIME_OUT, "timeout"} +}; + +static map link_training_rx_status_map = +{ + { SAI_PORT_LINK_TRAINING_RX_STATUS_NOT_TRAINED, "not_trained" }, + { SAI_PORT_LINK_TRAINING_RX_STATUS_TRAINED, "trained"} +}; + // Interface type map used for gearbox static map interface_type_map = { @@ -331,7 +353,8 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vector (new LagIdAllocator(chassisAppDb)); } + + auto executor = new ExecutableTimer(m_port_state_poller, this, "PORT_STATE_POLLER"); + Orch::addExecutor(executor); } void PortsOrch::removeDefaultVlanMembers() @@ -1985,6 +2011,35 @@ 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 + { + // To avoid breakage on the existing platforms, AN should be 1 by default + port.m_cap_an = 1; + SWSS_LOG_WARN("Unable to get %s AN support capability", + port.m_alias.c_str()); + } +} + +void PortsOrch::initPortCapLinkTraining(Port &port) +{ + // TODO: + // Add SAI_PORT_ATTR_SUPPORTED_LINK_TRAINING_MODE query when it is + // available in the saiport.h of SAI. + port.m_cap_lt = 1; + SWSS_LOG_WARN("Unable to get %s LT support capability", port.m_alias.c_str()); +} + /* * If Gearbox is enabled and this is a Gearbox port then set the attributes accordingly. */ @@ -2139,6 +2194,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(); @@ -2253,6 +2347,32 @@ task_process_status PortsOrch::setPortAutoNeg(sai_object_id_t id, int an) return task_success; } +task_process_status PortsOrch::setPortLinkTraining(const Port &port, bool state) +{ + SWSS_LOG_ENTER(); + + if (port.m_type != Port::PHY) + { + return task_failed; + } + + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_LINK_TRAINING_ENABLE; + attr.value.booldata = state; + + string op = state ? "on" : "off"; + sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set LT %s to port %s", op.c_str(), port.m_alias.c_str()); + return handleSaiSetStatus(SAI_API_PORT, status); + } + + SWSS_LOG_INFO("Set LT %s to port %s", op.c_str(), port.m_alias.c_str()); + + return task_success; +} + bool PortsOrch::setHostIntfsOperStatus(const Port& port, bool isUp) const { SWSS_LOG_ENTER(); @@ -2760,7 +2880,9 @@ void PortsOrch::doPortTask(Consumer &consumer) uint32_t speed = 0; string learn_mode; string an_str; + string lt_str; int an = -1; + int lt = -1; int index = -1; string role; string adv_speeds_str; @@ -2851,6 +2973,11 @@ void PortsOrch::doPortTask(Consumer &consumer) { adv_interface_types_str = fvValue(i); } + /* Set link training */ + else if (fvField(i) == "link_training") + { + lt_str = fvValue(i); + } /* Set port serdes Pre-emphasis */ else if (fvField(i) == "preemphasis") { @@ -3030,10 +3157,21 @@ void PortsOrch::doPortTask(Consumer &consumer) it = consumer.m_toSync.erase(it); continue; } - an = autoneg_mode_map[an_str]; if (an != p.m_autoneg) { + 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 (cap=%d)", p.m_alias.c_str(), p.m_cap_an); + // autoneg is not supported, don't retry + it = consumer.m_toSync.erase(it); + continue; + } if (p.m_admin_state_up) { /* Bring port down before applying speed */ @@ -3065,6 +3203,62 @@ 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 = static_cast(an); m_portList[alias] = p; + m_portStateTable.hdel(p.m_alias, "rmt_adv_speeds"); + updatePortStatePoll(p, PORT_STATE_POLL_AN, (an > 0)); + } + } + + if (!lt_str.empty() && (p.m_type == Port::PHY)) + { + if (link_training_mode_map.find(lt_str) == link_training_mode_map.end()) + { + SWSS_LOG_ERROR("Failed to parse LT value: %s", lt_str.c_str()); + // Invalid link training mode configured, don't retry + it = consumer.m_toSync.erase(it); + continue; + } + + lt = link_training_mode_map[lt_str]; + if (lt != p.m_link_training) + { + if (p.m_cap_lt < 0) + { + initPortCapLinkTraining(p); + m_portList[alias] = p; + } + if (p.m_cap_lt < 1) + { + SWSS_LOG_WARN("%s: LT is not supported(cap=%d)", alias.c_str(), p.m_cap_lt); + // Don't retry + it = consumer.m_toSync.erase(it); + continue; + } + + auto status = setPortLinkTraining(p, lt > 0 ? true : false); + if (status != task_success) + { + SWSS_LOG_ERROR("Failed to set port %s LT from %d to %d", alias.c_str(), p.m_link_training, lt); + if (status == task_need_retry) + { + it++; + } + else + { + it = consumer.m_toSync.erase(it); + } + continue; + } + m_portStateTable.hset(alias, "link_training_status", lt_str); + SWSS_LOG_NOTICE("Set port %s LT from %d to %d", alias.c_str(), p.m_link_training, lt); + p.m_link_training = lt; + m_portList[alias] = p; + updatePortStatePoll(p, PORT_STATE_POLL_LT, (lt > 0)); + + // Restore pre-emphasis when LT is transitioned from ON to OFF + if ((p.m_link_training < 1) && (serdes_attr.size() == 0)) + { + serdes_attr = p.m_preemphasis; + } } } @@ -3405,9 +3599,17 @@ void PortsOrch::doPortTask(Consumer &consumer) if (serdes_attr.size() != 0) { - if (setPortSerdesAttribute(p.m_port_id, serdes_attr)) + if (p.m_link_training > 0) + { + SWSS_LOG_NOTICE("Save port %s preemphasis for LT", alias.c_str()); + p.m_preemphasis = serdes_attr; + m_portList[alias] = p; + } + else if (setPortSerdesAttribute(p.m_port_id, serdes_attr)) { - SWSS_LOG_NOTICE("Set port %s preemphasis is success", alias.c_str()); + SWSS_LOG_NOTICE("Set port %s preemphasis is success", alias.c_str()); + p.m_preemphasis = serdes_attr; + m_portList[alias] = p; } else { @@ -3415,7 +3617,6 @@ void PortsOrch::doPortTask(Consumer &consumer) it++; continue; } - } /* create host_tx_ready field in state-db */ @@ -5957,6 +6158,18 @@ void PortsOrch::updatePortOperStatus(Port &port, sai_port_oper_status_t status) { updateDbPortOperStatus(port, status); updateGearboxPortOperStatus(port); + + /* Refresh the port states and reschedule the poller tasks */ + if (port.m_autoneg > 0) + { + refreshPortStateAutoNeg(port); + updatePortStatePoll(port, PORT_STATE_POLL_AN, !(status == SAI_PORT_OPER_STATUS_UP)); + } + if (port.m_link_training > 0) + { + refreshPortStateLinkTraining(port); + updatePortStatePoll(port, PORT_STATE_POLL_LT, !(status == SAI_PORT_OPER_STATUS_UP)); + } } port.m_oper_status = status; @@ -6112,6 +6325,50 @@ bool PortsOrch::getPortOperSpeed(const Port& port, sai_uint32_t& speed) const return true; } +bool PortsOrch::getPortLinkTrainingRxStatus(const Port &port, sai_port_link_training_rx_status_t &rx_status) +{ + SWSS_LOG_ENTER(); + + if (port.m_type != Port::PHY) + { + return false; + } + + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_LINK_TRAINING_RX_STATUS; + sai_status_t ret = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); + if (ret != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get LT rx status for %s", port.m_alias.c_str()); + return false; + } + + rx_status = static_cast(attr.value.u32); + return true; +} + +bool PortsOrch::getPortLinkTrainingFailure(const Port &port, sai_port_link_training_failure_status_t &failure) +{ + SWSS_LOG_ENTER(); + + if (port.m_type != Port::PHY) + { + return false; + } + + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_LINK_TRAINING_FAILURE_STATUS; + sai_status_t ret = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); + if (ret != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get LT failure status for %s", port.m_alias.c_str()); + return false; + } + + failure = static_cast(attr.value.u32); + return true; +} + bool PortsOrch::getSaiAclBindPointType(Port::Type type, sai_acl_bind_point_type_t &sai_acl_bind_type) { @@ -7154,3 +7411,119 @@ bool PortsOrch::decrFdbCount(const std::string& alias, int count) } return true; } + +/* Refresh the per-port Auto-Negotiation operational states */ +void PortsOrch::refreshPortStateAutoNeg(const Port &port) +{ + SWSS_LOG_ENTER(); + + if (port.m_type != Port::Type::PHY) + { + return; + } + + string adv_speeds = "N/A"; + + if (port.m_admin_state_up) + { + if (!getPortAdvSpeeds(port, true, adv_speeds)) + { + adv_speeds = "N/A"; + updatePortStatePoll(port, PORT_STATE_POLL_AN, false); + } + } + + m_portStateTable.hset(port.m_alias, "rmt_adv_speeds", adv_speeds); +} + +/* Refresh the per-port Link-Training operational states */ +void PortsOrch::refreshPortStateLinkTraining(const Port &port) +{ + SWSS_LOG_ENTER(); + + if (port.m_type != Port::Type::PHY) + { + return; + } + + string status = "off"; + + if (port.m_admin_state_up && port.m_link_training > 0 && port.m_cap_lt > 0) + { + sai_port_link_training_rx_status_t rx_status; + sai_port_link_training_failure_status_t failure; + + if (!getPortLinkTrainingRxStatus(port, rx_status)) + { + status = "on"; // LT is enabled, while the rx status is unavailable + } + else if (rx_status == SAI_PORT_LINK_TRAINING_RX_STATUS_TRAINED) + { + status = link_training_rx_status_map.at(rx_status); + } + else + { + if (getPortLinkTrainingFailure(port, failure) && + failure != SAI_PORT_LINK_TRAINING_FAILURE_STATUS_NO_ERROR) + { + status = link_training_failure_map.at(failure); + } + else + { + status = link_training_rx_status_map.at(rx_status); + } + } + } + + m_portStateTable.hset(port.m_alias, "link_training_status", status); +} + +/* Activate/De-activate a specific port state poller task */ +void PortsOrch::updatePortStatePoll(const Port &port, port_state_poll_t type, bool active) +{ + if (type == PORT_STATE_POLL_NONE) + { + return; + } + if (active) + { + m_port_state_poll[port.m_alias] |= type; + m_port_state_poller->start(); + } + else + { + m_port_state_poll[port.m_alias] &= ~type; + } +} + +void PortsOrch::doTask(swss::SelectableTimer &timer) +{ + Port port; + + for (auto it = m_port_state_poll.begin(); it != m_port_state_poll.end(); ) + { + if ((it->second == PORT_STATE_POLL_NONE) || !getPort(it->first, port)) + { + it = m_port_state_poll.erase(it); + continue; + } + if (!port.m_admin_state_up) + { + ++it; + continue; + } + if (it->second & PORT_STATE_POLL_AN) + { + refreshPortStateAutoNeg(port); + } + if (it->second & PORT_STATE_POLL_LT) + { + refreshPortStateLinkTraining(port); + } + ++it; + } + if (m_port_state_poll.size() == 0) + { + m_port_state_poller->stop(); + } +} diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 0fd3552e1982..42e17d7fb94c 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -258,6 +258,8 @@ class PortsOrch : public Orch, public Subject NotificationConsumer* m_portStatusNotificationConsumer; + swss::SelectableTimer *m_port_state_poller = nullptr; + void doTask() override; void doTask(Consumer &consumer); void doPortTask(Consumer &consumer); @@ -267,6 +269,7 @@ class PortsOrch : public Orch, public Subject void doLagMemberTask(Consumer &consumer); void doTask(NotificationConsumer &consumer); + void doTask(swss::SelectableTimer &timer); void removePortFromLanesMap(string alias); void removePortFromPortListMap(sai_object_id_t port_id); @@ -299,6 +302,9 @@ 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); bool getPortAdminStatus(sai_object_id_t id, bool& up); bool setPortMtu(sai_object_id_t id, sai_uint32_t mtu); @@ -319,6 +325,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); @@ -338,12 +346,27 @@ class PortsOrch : public Orch, public Subject bool setPortFecMode(sai_object_id_t id, int fec); task_process_status setPortInterfaceType(sai_object_id_t id, sai_port_interface_type_t interface_type); task_process_status setPortAdvInterfaceTypes(sai_object_id_t id, std::vector &interface_types); + task_process_status setPortLinkTraining(const Port& port, bool state); void updatePortOperStatus(Port &port, sai_port_oper_status_t status); bool getPortOperSpeed(const Port& port, sai_uint32_t& speed) const; void updateDbPortOperSpeed(Port &port, sai_uint32_t speed); + bool getPortLinkTrainingRxStatus(const Port &port, sai_port_link_training_rx_status_t &rx_status); + bool getPortLinkTrainingFailure(const Port &port, sai_port_link_training_failure_status_t &failure); + + typedef enum { + PORT_STATE_POLL_NONE = 0, + PORT_STATE_POLL_AN = 0x00000001, /* Auto Negotiation */ + PORT_STATE_POLL_LT = 0x00000002 /* Link Trainig */ + } port_state_poll_t; + + map m_port_state_poll; + void updatePortStatePoll(const Port &port, port_state_poll_t type, bool active); + void refreshPortStateAutoNeg(const Port &port); + void refreshPortStateLinkTraining(const Port &port); + void getPortSerdesVal(const std::string& s, std::vector &lane_values); bool getPortAdvSpeedsVal(const std::string &s, std::vector &speed_values); bool getPortInterfaceTypeVal(const std::string &s, sai_port_interface_type_t &interface_type); diff --git a/tests/test_port_an.py b/tests/test_port_an.py index dc98f43d0e40..9c004aa790fc 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 diff --git a/tests/test_port_lt.py b/tests/test_port_lt.py new file mode 100644 index 000000000000..3ec51ed68b11 --- /dev/null +++ b/tests/test_port_lt.py @@ -0,0 +1,139 @@ +import time +import os +import pytest + +from swsscommon import swsscommon + + +class TestPortLinkTraining(object): + def test_PortLinkTrainingForce(self, dvs, testlog): + + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + adb = dvs.get_asic_db() + + tbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") + fvs = swsscommon.FieldValuePairs([("link_training","off")]) + tbl.set("Ethernet0", fvs) + + tbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") + fvs = swsscommon.FieldValuePairs([("link_training","on")]) + tbl.set("Ethernet4", fvs) + + # validate if link_training false is pushed to asic db when set first time + port_oid = adb.port_name_map["Ethernet0"] + expected_fields = {"SAI_PORT_ATTR_LINK_TRAINING_ENABLE":"false"} + adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) + + # validate if link_training true is pushed to asic db when set first time + port_oid = adb.port_name_map["Ethernet4"] + expected_fields = {"SAI_PORT_ATTR_LINK_TRAINING_ENABLE":"true"} + adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) + + def test_PortLinkTrainingCold(self, dvs, testlog): + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + + tbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") + + # set link_training = true + fvs = swsscommon.FieldValuePairs([("link_training","on")]) + + tbl.set("Ethernet0", fvs) + + time.sleep(1) + + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + assert "SAI_PORT_ATTR_LINK_TRAINING_ENABLE" in [fv[0] for fv in fvs] + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_LINK_TRAINING_ENABLE": + assert fv[1] == "true" + + # change link_training to false + fvs = swsscommon.FieldValuePairs([("link_training","off")]) + + tbl.set("Ethernet0", fvs) + + time.sleep(1) + + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + assert "SAI_PORT_ATTR_LINK_TRAINING_ENABLE" in [fv[0] for fv in fvs] + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_LINK_TRAINING_ENABLE": + assert fv[1] == "false" + + def test_PortLinkTrainingWarm(self, dvs, testlog): + + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + sdb = swsscommon.DBConnector(6, dvs.redis_sock, 0) + + tbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") + ctbl = swsscommon.Table(cdb, "PORT") + stbl = swsscommon.Table(sdb, "PORT_TABLE") + + # set link_training = true + fvs = swsscommon.FieldValuePairs([("link_training","on")]) + ctbl.set("Ethernet0", fvs) + + time.sleep(1) + + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + assert "SAI_PORT_ATTR_LINK_TRAINING_ENABLE" in [fv[0] for fv in fvs] + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": + assert fv[1] == "true" + + # set admin up + cfvs = swsscommon.FieldValuePairs([("admin_status", "up")]) + ctbl.set("Ethernet0", cfvs) + + # enable warm restart + (exitcode, result) = dvs.runcmd("config warm_restart enable swss") + assert exitcode == 0 + + # freeze orchagent for warm restart + (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check") + assert result == "RESTARTCHECK succeeded\n" + time.sleep(2) + + try: + # restart orchagent + # clean port state + dvs.stop_swss() + ports = stbl.getKeys() + for port in ports: + stbl._del(port) + dvs.start_swss() + time.sleep(2) + + # check ASIC DB after warm restart + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + assert "SAI_PORT_ATTR_LINK_TRAINING_ENABLE" in [fv[0] for fv in fvs] + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_LINK_TRAINING_ENABLE": + assert fv[1] == "true" + + finally: + # disable warm restart + dvs.runcmd("config warm_restart disable swss") + # slow down crm polling + dvs.runcmd("crm config polling interval 10000") + + +# Add Dummy always-pass test at end as workaroud +# for issue when Flaky fail on final test it invokes module tear-down before retrying +def test_nonflaky_dummy(): + pass