Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions orchagent/orchdaemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,13 @@ bool OrchDaemon::init()
TableConnector conf_asic_sensors(m_configDb, CFG_ASIC_SENSORS_TABLE_NAME);
TableConnector conf_switch_hash(m_configDb, CFG_SWITCH_HASH_TABLE_NAME);
TableConnector conf_switch_trim(m_configDb, CFG_SWITCH_TRIMMING_TABLE_NAME);
TableConnector conf_switch_fast_linkup(m_configDb, CFG_SWITCH_FAST_LINKUP_TABLE_NAME);
TableConnector conf_suppress_asic_sdk_health_categories(m_configDb, CFG_SUPPRESS_ASIC_SDK_HEALTH_EVENT_NAME);

vector<TableConnector> switch_tables = {
conf_switch_hash,
conf_switch_trim,
conf_switch_fast_linkup,
conf_asic_sensors,
conf_suppress_asic_sdk_health_categories,
app_switch_table
Expand Down
5 changes: 5 additions & 0 deletions orchagent/port/portcnt.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ class PortConfig final
bool is_set = false;
} link_training; // Port link training

struct {
bool value;
bool is_set = false;
} fast_linkup; // Port fast link-up enable

struct {

struct {
Expand Down
33 changes: 33 additions & 0 deletions orchagent/port/porthlpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,32 @@ bool PortHelper::parsePortLinkTraining(PortConfig &port, const std::string &fiel
return true;
}

bool PortHelper::parsePortFastLinkup(PortConfig &port, const std::string &field, const std::string &value) const
{
SWSS_LOG_ENTER();

if (value.empty())
{
SWSS_LOG_ERROR("Failed to parse field(%s): empty value is not allowed", field.c_str());
return false;
}
if (value == "true")
{
port.fast_linkup.value = true;
}
else if (value == "false")
{
port.fast_linkup.value = false;
}
else
{
SWSS_LOG_ERROR("Failed to parse field(%s): invalid value(%s)", field.c_str(), value.c_str());
return false;
}
port.fast_linkup.is_set = true;
return true;
}

template<typename T>
bool PortHelper::parsePortSerdes(T &serdes, const std::string &field, const std::string &value) const
{
Expand Down Expand Up @@ -1056,6 +1082,13 @@ bool PortHelper::parsePortConfig(PortConfig &port) const
return false;
}
}
else if (field == PORT_FAST_LINKUP)
{
if (!this->parsePortFastLinkup(port, field, value))
{
return false;
}
}
else if (field == PORT_UNRELIABLE_LOS)
{
if (!this->parsePortUnreliableLos(port, field, value))
Expand Down
1 change: 1 addition & 0 deletions orchagent/port/porthlpr.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,5 @@ class PortHelper final
bool parsePortSubport(PortConfig &port, const std::string &field, const std::string &value) const;
bool parsePortPtIntfId(PortConfig &port, const std::string &field, const std::string &value) const;
bool parsePortPtTimestampTemplate(PortConfig &port, const std::string &field, const std::string &value) const;
bool parsePortFastLinkup(PortConfig &port, const std::string &field, const std::string &value) const;
};
1 change: 1 addition & 0 deletions orchagent/port/portschema.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,4 @@
#define PORT_FLAP_PENALTY "flap_penalty"
#define PORT_MODE "mode"
#define PORT_UNRELIABLE_LOS "unreliable_los"
#define PORT_FAST_LINKUP "fast_linkup"
41 changes: 41 additions & 0 deletions orchagent/portsorch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,12 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vector<table_name_wi
m_defaultVlan = attrs[1].value.oid;
}

//Fast link-up per-port attribute support
if (gSwitchOrch->querySwitchCapability(SAI_OBJECT_TYPE_PORT, SAI_PORT_ATTR_FAST_LINKUP_ENABLED))
{
m_fastLinkupPortAttrSupported = true;
}

if (gMySwitchType != "dpu")
{
// System Ports not supported on dpu
Expand Down Expand Up @@ -3424,6 +3430,28 @@ bool PortsOrch::getPortAdvSpeeds(const Port& port, bool remote, string& adv_spee
return rc;
}

task_process_status PortsOrch::setPortFastLinkupEnabled(Port &port, bool enable)
{
SWSS_LOG_ENTER();

if (!m_fastLinkupPortAttrSupported)
{
SWSS_LOG_NOTICE("Fast link-up is not supported on this platform");
return task_success;
}
sai_attribute_t attr;
attr.id = SAI_PORT_ATTR_FAST_LINKUP_ENABLED;
attr.value.booldata = enable;
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 fast_linkup %d on port %s", enable, port.m_alias.c_str());
return handleSaiSetStatus(SAI_API_PORT, status);
}
SWSS_LOG_INFO("Set port %s fast_linkup %s", port.m_alias.c_str(), enable ? "true" : "false");
return task_success;
}

task_process_status PortsOrch::setPortUnreliableLOS(Port &port, bool enabled)
{
SWSS_LOG_ENTER();
Expand Down Expand Up @@ -4598,6 +4626,19 @@ void PortsOrch::doPortTask(Consumer &consumer)
}
}

// Handle fast_linkup boolean
if (pCfg.fast_linkup.is_set)
{
auto status = setPortFastLinkupEnabled(p, pCfg.fast_linkup.value);
// For fast_linkup attribute, task_need_retry is not a meaningful return, so treat any failure as a permanent failure and erase the task.
if (status != task_success)
{
SWSS_LOG_ERROR("Failed to set port %s fast_linkup to %s", p.m_alias.c_str(), pCfg.fast_linkup.value ? "true" : "false");
it = taskMap.erase(it);
continue;
}
}

if (pCfg.link_event_damping_algorithm.is_set)
{
if (p.m_link_event_damping_algorithm != pCfg.link_event_damping_algorithm.value)
Expand Down
2 changes: 2 additions & 0 deletions orchagent/portsorch.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ class PortsOrch : public Orch, public Subject

bool setPortPtIntfId(const Port& port, sai_uint16_t intf_id);
bool setPortPtTimestampTemplate(const Port& port, sai_port_path_tracing_timestamp_type_t ts_type);
task_process_status setPortFastLinkupEnabled(Port &port, bool enable);

private:
unique_ptr<CounterNameMapUpdater> m_counterNameMapUpdater;
Expand Down Expand Up @@ -365,6 +366,7 @@ class PortsOrch : public Orch, public Subject
bool saiHwTxSignalSupported = false;
bool saiTxReadyNotifySupported = false;
bool m_supportsHostIfTxQueue = false;
bool m_fastLinkupPortAttrSupported = false;

swss::SelectableTimer *m_port_state_poller = nullptr;

Expand Down
195 changes: 195 additions & 0 deletions orchagent/switchorch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ SwitchOrch::SwitchOrch(DBConnector *db, vector<TableConnector>& connectors, Tabl
querySwitchPortEgressSampleCapability();
querySwitchHashDefaults();
setSwitchIcmpOffloadCapability();
setFastLinkupCapability();

auto executorT = new ExecutableTimer(m_sensorsPollerTimer, this, "ASIC_SENSORS_POLL_TIMER");
Orch::addExecutor(executorT);
Expand Down Expand Up @@ -1475,6 +1476,10 @@ void SwitchOrch::doTask(Consumer &consumer)
{
doCfgSwitchTrimmingTableTask(consumer);
}
else if (tableName == CFG_SWITCH_FAST_LINKUP_TABLE_NAME)
{
doCfgSwitchFastLinkupTableTask(consumer);
}
else if (tableName == CFG_SUPPRESS_ASIC_SDK_HEALTH_EVENT_NAME)
{
doCfgSuppressAsicSdkHealthEventTableTask(consumer);
Expand Down Expand Up @@ -1993,6 +1998,196 @@ bool SwitchOrch::querySwitchCapability(sai_object_type_t sai_object, sai_attr_id
}
}

void SwitchOrch::setFastLinkupCapability()
{
SWSS_LOG_ENTER();

std::vector<FieldValueTuple> fvVector;

// Determine support by checking create/set capability on polling time attribute (enabled in real SAI)
bool supported = querySwitchCapability(SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_FAST_LINKUP_POLLING_TIMEOUT);
m_fastLinkupCap.supported = supported;

if (!supported)
{
fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_FAST_LINKUP_CAPABLE, "false");
set_switch_capability(fvVector);
return;
}

fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_FAST_LINKUP_CAPABLE, "true");

// Query allowed ranges if supported by SAI
{
sai_attribute_t attr;
attr.id = SAI_SWITCH_ATTR_FAST_LINKUP_POLLING_TIMEOUT_RANGE;
sai_status_t status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr);
if (status == SAI_STATUS_SUCCESS)
{
m_fastLinkupCap.has_ranges = true;
m_fastLinkupCap.polling_min = attr.value.u16range.min;
m_fastLinkupCap.polling_max = attr.value.u16range.max;
fvVector.emplace_back(
SWITCH_CAPABILITY_TABLE_FAST_LINKUP_POLLING_TIMER_RANGE,
to_string(m_fastLinkupCap.polling_min) + "," + to_string(m_fastLinkupCap.polling_max));
}
else
{
SWSS_LOG_ERROR("Failed to get fast linkup polling range: %s", sai_serialize_status(status).c_str());
}
}

{
sai_attribute_t attr;
attr.id = SAI_SWITCH_ATTR_FAST_LINKUP_GUARD_TIMEOUT_RANGE;
sai_status_t status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr);
if (status == SAI_STATUS_SUCCESS)
{
m_fastLinkupCap.has_ranges = true;
m_fastLinkupCap.guard_min = attr.value.u16range.min;
m_fastLinkupCap.guard_max = attr.value.u16range.max;
fvVector.emplace_back(
SWITCH_CAPABILITY_TABLE_FAST_LINKUP_GUARD_TIMER_RANGE,
to_string(m_fastLinkupCap.guard_min) + "," + to_string(m_fastLinkupCap.guard_max));
}
else
{
SWSS_LOG_ERROR("Failed to get fast linkup guard range: %s", sai_serialize_status(status).c_str());
}
}
set_switch_capability(fvVector);
}

bool SwitchOrch::setSwitchFastLinkup(const FastLinkupConfig &cfg)
{
SWSS_LOG_ENTER();

if (!m_fastLinkupCap.supported)
{
SWSS_LOG_NOTICE("Fast link-up is not supported on this platform");
return false;
}

// Validate ranges if known
if (cfg.has_polling && m_fastLinkupCap.has_ranges)
{
if (cfg.polling_time < m_fastLinkupCap.polling_min || cfg.polling_time > m_fastLinkupCap.polling_max)
{
SWSS_LOG_NOTICE("Invalid polling_time %u; allowed [%u,%u]", cfg.polling_time, m_fastLinkupCap.polling_min, m_fastLinkupCap.polling_max);
return false;
}
}
if (cfg.has_guard && m_fastLinkupCap.has_ranges)
{
if (cfg.guard_time < m_fastLinkupCap.guard_min || cfg.guard_time > m_fastLinkupCap.guard_max)
{
SWSS_LOG_NOTICE("Invalid guard_time %u; allowed [%u,%u]", cfg.guard_time, m_fastLinkupCap.guard_min, m_fastLinkupCap.guard_max);
return false;
}
}

// Apply attributes conditionally
sai_status_t status;
if (cfg.has_polling)
{
sai_attribute_t attr;
attr.id = SAI_SWITCH_ATTR_FAST_LINKUP_POLLING_TIMEOUT;
attr.value.u16 = cfg.polling_time;
status = sai_switch_api->set_switch_attribute(gSwitchId, &attr);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_NOTICE("Failed to set FAST_LINKUP_POLLING_TIME=%u: %s", cfg.polling_time, sai_serialize_status(status).c_str());
return false;
}
}

if (cfg.has_guard)
{
sai_attribute_t attr;
attr.id = SAI_SWITCH_ATTR_FAST_LINKUP_GUARD_TIMEOUT;
attr.value.u8 = cfg.guard_time;
status = sai_switch_api->set_switch_attribute(gSwitchId, &attr);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_NOTICE("Failed to set FAST_LINKUP_GUARD_TIME=%u: %s", cfg.guard_time, sai_serialize_status(status).c_str());
return false;
}
}

if (cfg.has_ber)
{
sai_attribute_t attr;
attr.id = SAI_SWITCH_ATTR_FAST_LINKUP_BER_THRESHOLD;
attr.value.u8 = cfg.ber_threshold;
status = sai_switch_api->set_switch_attribute(gSwitchId, &attr);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_NOTICE("Failed to set FAST_LINKUP_BER_THRESHOLD=%u: %s", cfg.ber_threshold, sai_serialize_status(status).c_str());
return false;
}
}
SWSS_LOG_INFO("Fast link-up set: polling_time=%s, guard_time=%s, ber_threshold=%s",
cfg.has_polling ? std::to_string(cfg.polling_time).c_str() : "N/A",
cfg.has_guard ? std::to_string(cfg.guard_time).c_str() : "N/A",
cfg.has_ber ? std::to_string(cfg.ber_threshold).c_str() : "N/A");
return true;
}

void SwitchOrch::doCfgSwitchFastLinkupTableTask(Consumer &consumer)
{
SWSS_LOG_ENTER();

auto &map = consumer.m_toSync;
auto it = map.begin();

while (it != map.end())
{
auto keyOpFieldsValues = it->second;
auto key = kfvKey(keyOpFieldsValues);
auto op = kfvOp(keyOpFieldsValues);

if (op == SET_COMMAND)
{
FastLinkupConfig cfg;
for (const auto &cit : kfvFieldsValues(keyOpFieldsValues))
{
auto fieldName = fvField(cit);
auto fieldValue = fvValue(cit);
if (fieldName == "polling_time")
{
try { cfg.polling_time = to_uint<uint16_t>(fieldValue); cfg.has_polling = true; }
catch (...) { SWSS_LOG_ERROR("Invalid polling_time value %s", fieldValue.c_str()); }
}
else if (fieldName == "guard_time")
{
try { cfg.guard_time = to_uint<uint8_t>(fieldValue); cfg.has_guard = true; }
catch (...) { SWSS_LOG_ERROR("Invalid guard_time value %s", fieldValue.c_str()); }
}
else if (fieldName == "ber_threshold")
{
try { cfg.ber_threshold = to_uint<uint8_t>(fieldValue); cfg.has_ber = true; }
catch (...) { SWSS_LOG_ERROR("Invalid ber_threshold value %s", fieldValue.c_str()); }
}
else
{
SWSS_LOG_WARN("Unknown field %s in SWITCH_FAST_LINKUP", fieldName.c_str());
}
}

if (!setSwitchFastLinkup(cfg))
{
SWSS_LOG_ERROR("Failed to configure fast link-up from CONFIG_DB");
}
}
else
{
SWSS_LOG_ERROR("Unsupported operation %s for SWITCH_FAST_LINKUP", op.c_str());
}

it = map.erase(it);
}
}

// Bind ACL table (with bind type switch) to switch
bool SwitchOrch::bindAclTableToSwitch(acl_stage_type_t stage, sai_object_id_t table_id)
{
Expand Down
Loading