Skip to content

Commit

Permalink
portsorch: initial support for link-training
Browse files Browse the repository at this point in the history
- 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 <dante.su@broadcom.com>
  • Loading branch information
ds952811 committed May 19, 2022
1 parent bbbd5f4 commit c4345ef
Show file tree
Hide file tree
Showing 4 changed files with 386 additions and 1 deletion.
1 change: 1 addition & 0 deletions orchagent/port.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class Port
uint32_t m_speed = 0; // Mbps
std::string m_learn_mode = "hardware";
int m_autoneg = -1; // -1 means not set, 0 = disabled, 1 = enabled
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;
Expand Down
235 changes: 234 additions & 1 deletion orchagent/portsorch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ extern string gMyAsicName;
#define DEFAULT_VLAN_ID 1
#define MAX_VALID_VLAN_ID 4094

#define PORT_STAT_POLLING_SEC 3
#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
Expand Down Expand Up @@ -112,6 +113,26 @@ static map<string, int> autoneg_mode_map =
{ "off", 0 }
};

static map<string, int> link_training_mode_map =
{
{ "on", 1 },
{ "off", 0 }
};

static map<sai_port_link_training_failure_status_t, string> 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<sai_port_link_training_rx_status_t, string> 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<string, sai_port_interface_type_t> interface_type_map =
{
Expand Down Expand Up @@ -304,9 +325,11 @@ static bool isValidPortTypeForLagMember(const Port& port)
PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vector<table_name_with_pri_t> &tableNames, DBConnector *chassisAppDb) :
Orch(db, tableNames),
m_portStateTable(stateDb, STATE_PORT_TABLE_NAME),
m_stateLinkTrainingTable(stateDb, STATE_LINK_TRAINING_TABLE_NAME),
port_stat_manager(PORT_STAT_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, PORT_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS, false),
port_buffer_drop_stat_manager(PORT_BUFFER_DROP_STAT_FLEX_COUNTER_GROUP, StatsMode::READ, PORT_BUFFER_DROP_STAT_POLLING_INTERVAL_MS, false),
queue_stat_manager(QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, QUEUE_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS, false)
queue_stat_manager(QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, QUEUE_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS, false),
m_timer(new SelectableTimer(timespec { .tv_sec = PORT_STAT_POLLING_SEC, .tv_nsec = 0 }))
{
SWSS_LOG_ENTER();

Expand Down Expand Up @@ -571,6 +594,10 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vector<table_name_wi

m_lagIdAllocator = unique_ptr<LagIdAllocator> (new LagIdAllocator(chassisAppDb));
}

auto executor = new ExecutableTimer(m_timer, this, "PORT_STATUS_POLL");
Orch::addExecutor(executor);
m_timer->start();
}

void PortsOrch::removeDefaultVlanMembers()
Expand Down Expand Up @@ -1905,6 +1932,24 @@ void PortsOrch::initPortSupportedSpeeds(const std::string& alias, sai_object_id_
m_portStateTable.set(alias, v);
}

void PortsOrch::initPortCapabilityLinkTraining(const Port &port)
{
sai_status_t status;
sai_attribute_t attr;

attr.id = SAI_PORT_ATTR_SUPPORTED_LINK_TRAINING_MODE;
status = sai_port_api->set_port_attribute(port.m_port_id, &attr);
if (status == SAI_STATUS_SUCCESS)
{
string supp = attr.value.booldata ? "yes" : "no";
m_stateLinkTrainingTable.hset(port.m_alias, "supported", supp);
}
else
{
SWSS_LOG_NOTICE("Unable to get %s LT capability", port.m_alias.c_str());
}
}

/*
* If Gearbox is enabled and this is a Gearbox port then set the attributes accordingly.
*/
Expand Down Expand Up @@ -2173,6 +2218,44 @@ task_process_status PortsOrch::setPortAutoNeg(sai_object_id_t id, int an)
return task_success;
}

bool PortsOrch::getPortCapabilityLinkTraining(const Port& port)
{
string supp;

if (m_stateLinkTrainingTable.hget(port.m_alias, "supported", supp))
{
return (supp == "yes") ? true : false;
}
return false;
}

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 pid:%" PRIx64,
op.c_str(), port.m_port_id);
return handleSaiSetStatus(SAI_API_PORT, status);
}

SWSS_LOG_INFO("Set LT %s to port pid:%" PRIx64, op.c_str(), port.m_port_id);

return task_success;
}

bool PortsOrch::setHostIntfsOperStatus(const Port& port, bool isUp) const
{
SWSS_LOG_ENTER();
Expand Down Expand Up @@ -2396,6 +2479,9 @@ bool PortsOrch::initPort(const string &alias, const string &role, const int inde
/* Create associated Gearbox lane mapping */
initGearboxPort(p);

/* Initialize port capabilities */
initPortCapabilityLinkTraining(p);

/* Add port to port list */
m_portList[alias] = p;
saiOidToAlias[id] = alias;
Expand Down Expand Up @@ -2672,7 +2758,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;
Expand Down Expand Up @@ -2763,6 +2851,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")
{
Expand Down Expand Up @@ -2975,6 +3068,48 @@ void PortsOrch::doPortTask(Consumer &consumer)
}
}

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 (!getPortCapabilityLinkTraining(p))
{
SWSS_LOG_ERROR("%s: LT is not supported by the ASIC", alias.c_str());
// 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_stateLinkTrainingTable.hset(p.m_alias, "status", lt > 0 ? "on" : "off");
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;
}
}

if (speed != 0)
{
if (speed != p.m_speed)
Expand Down Expand Up @@ -5958,6 +6093,54 @@ 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;
attr.value.u32 = 0;

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<sai_port_link_training_rx_status_t>(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;
attr.value.u32 = 0;

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<sai_port_link_training_failure_status_t>(attr.value.u32);
return true;
}

bool PortsOrch::getSaiAclBindPointType(Port::Type type,
sai_acl_bind_point_type_t &sai_acl_bind_type)
{
Expand Down Expand Up @@ -6943,3 +7126,53 @@ bool PortsOrch::decrFdbCount(const std::string& alias, int count)
}
return true;
}

void PortsOrch::updatePortLinkTrainingStatus(const Port &port)
{
SWSS_LOG_ENTER();

if (port.m_type != Port::Type::PHY)
{
return;
}

string status = "off";

if ((port.m_link_training > 0) && getPortCapabilityLinkTraining(port))
{
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_stateLinkTrainingTable.hset(port.m_alias, "status", status);
}

void PortsOrch::doTask(swss::SelectableTimer &timer)
{
SWSS_LOG_ENTER();

for (auto it = m_portList.begin(); it != m_portList.end(); ++it)
{
updatePortLinkTrainingStatus(it->second);
}
}
12 changes: 12 additions & 0 deletions orchagent/portsorch.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ class PortsOrch : public Orch, public Subject
unique_ptr<ProducerTable> m_flexCounterTable;
unique_ptr<ProducerTable> m_flexCounterGroupTable;
Table m_portStateTable;
Table m_stateLinkTrainingTable;

std::string getQueueWatermarkFlexCounterTableKey(std::string s);
std::string getPriorityGroupWatermarkFlexCounterTableKey(std::string s);
Expand Down Expand Up @@ -249,6 +250,8 @@ class PortsOrch : public Orch, public Subject

NotificationConsumer* m_portStatusNotificationConsumer;

swss::SelectableTimer *m_timer = nullptr;

void doTask() override;
void doTask(Consumer &consumer);
void doPortTask(Consumer &consumer);
Expand All @@ -258,6 +261,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);
Expand Down Expand Up @@ -290,6 +294,9 @@ class PortsOrch : public Orch, public Subject
bool initPort(const string &alias, const string &role, const int index, const set<int> &lane_set);
void deInitPort(string alias, sai_object_id_t port_id);

void initPortCapabilityLinkTraining(const Port &port);
bool getPortCapabilityLinkTraining(const 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);
Expand Down Expand Up @@ -329,12 +336,17 @@ 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<uint32_t> &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);
void updatePortLinkTrainingStatus(const Port &port);

void getPortSerdesVal(const std::string& s, std::vector<uint32_t> &lane_values);
bool getPortAdvSpeedsVal(const std::string &s, std::vector<uint32_t> &speed_values);
bool getPortInterfaceTypeVal(const std::string &s, sai_port_interface_type_t &interface_type);
Expand Down
Loading

0 comments on commit c4345ef

Please sign in to comment.