From cc155c61eff69e53044c1e3a55059b165b2ca4e3 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Fri, 22 May 2026 15:06:28 +0530 Subject: [PATCH 01/35] Adding link event damping support Signed-off-by: Sivakumar Thirukkanna Thevar --- lib/ClientSai.cpp | 3 +- lib/RedisRemoteSaiInterface.cpp | 96 + lib/RedisRemoteSaiInterface.h | 19 + lib/Sai.cpp | 6 + lib/sairediscommon.h | 2 + syncd/NotificationProcessor.cpp | 46 +- syncd/NotificationProcessor.h | 9 +- syncd/Syncd.cpp | 826 ++- syncd/Syncd.cpp.orig | 6105 +++++++++++++++++++++ syncd/Syncd.h | 177 + syncd/tests/Makefile.am | 2 +- syncd/tests/TestSyncdLinkEventDamping.cpp | 255 + unittest/lib/TestClientServerSai.cpp | 68 + 13 files changed, 7602 insertions(+), 12 deletions(-) create mode 100644 syncd/Syncd.cpp.orig create mode 100644 syncd/tests/TestSyncdLinkEventDamping.cpp diff --git a/lib/ClientSai.cpp b/lib/ClientSai.cpp index 63bc2392e2..06829109b8 100644 --- a/lib/ClientSai.cpp +++ b/lib/ClientSai.cpp @@ -240,7 +240,8 @@ sai_status_t ClientSai::set( SWSS_LOG_ENTER(); REDIS_CHECK_API_INITIALIZED(); - if (RedisRemoteSaiInterface::isRedisAttribute(objectType, attr)) + if (RedisRemoteSaiInterface::isRedisAttribute(objectType, attr) || + RedisRemoteSaiInterface::isRedisPortAttribute(objectType, attr)) { SWSS_LOG_ERROR("sairedis extension attributes are not supported in CLIENT mode"); diff --git a/lib/RedisRemoteSaiInterface.cpp b/lib/RedisRemoteSaiInterface.cpp index dabd7ad0b0..ada0201110 100644 --- a/lib/RedisRemoteSaiInterface.cpp +++ b/lib/RedisRemoteSaiInterface.cpp @@ -532,6 +532,83 @@ sai_status_t RedisRemoteSaiInterface::setRedisExtensionAttribute( return SAI_STATUS_FAILURE; } +sai_status_t RedisRemoteSaiInterface::setLinkEventDampingConfig( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const std::vector &values) +{ + SWSS_LOG_ENTER(); + + std::string key = sai_serialize_object_type(objectType) + ":" + sai_serialize_object_id(objectId); + + m_communicationChannel->set(key, values, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + if (m_syncMode) + { + swss::KeyOpFieldsValuesTuple kco; + auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, kco); + + m_recorder->recordGenericSetResponse(status); + + return status; + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t RedisRemoteSaiInterface::setRedisPortExtensionAttribute( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr) +{ + SWSS_LOG_ENTER(); + + if (attr == nullptr) + { + SWSS_LOG_ERROR("attr pointer is null"); + + return SAI_STATUS_INVALID_PARAMETER; + } + + std::string str_attr_id = sai_serialize_redis_port_attr_id( + static_cast(attr->id)); + + switch (attr->id) + { + case SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM: + { + std::string str_attr_value = sai_serialize_redis_link_event_damping_algorithm( + static_cast(attr->value.s32)); + + return setLinkEventDampingConfig( + objectType, objectId, {swss::FieldValueTuple(str_attr_id, str_attr_value)}); + } + case SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG: + { + sai_redis_link_event_damping_algo_aied_config_t *config = + (sai_redis_link_event_damping_algo_aied_config_t *)attr->value.ptr; + + if (config == NULL) + { + SWSS_LOG_ERROR("invalid link damping config attr value NULL"); + + return SAI_STATUS_INVALID_PARAMETER; + } + + std::string str_attr_value = sai_serialize_redis_link_event_damping_aied_config(*config); + + return setLinkEventDampingConfig( + objectType, objectId, {swss::FieldValueTuple(str_attr_id, str_attr_value)}); + } + default: + break; + } + + SWSS_LOG_ERROR("unknown redis port extension attribute: %d", attr->id); + + return SAI_STATUS_INVALID_PARAMETER; +} + bool RedisRemoteSaiInterface::isSaiS8ListValidString( _In_ const sai_s8_list_t &s8list) { @@ -666,6 +743,11 @@ sai_status_t RedisRemoteSaiInterface::set( return setRedisExtensionAttribute(objectType, objectId, attr); } + if (RedisRemoteSaiInterface::isRedisPortAttribute(objectType, attr)) + { + return setRedisPortExtensionAttribute(objectType, objectId, attr); + } + auto status = set( objectType, sai_serialize_object_id(objectId), @@ -2201,6 +2283,20 @@ bool RedisRemoteSaiInterface::isRedisAttribute( return true; } +bool RedisRemoteSaiInterface::isRedisPortAttribute( + _In_ sai_object_id_t objectType, + _In_ const sai_attribute_t* attr) +{ + SWSS_LOG_ENTER(); + + if ((objectType != SAI_OBJECT_TYPE_PORT) || (attr == nullptr) || (attr->id < SAI_PORT_ATTR_CUSTOM_RANGE_START)) + { + return false; + } + + return true; +} + void RedisRemoteSaiInterface::handleNotification( _In_ const std::string &name, _In_ const std::string &serializedNotification, diff --git a/lib/RedisRemoteSaiInterface.h b/lib/RedisRemoteSaiInterface.h index 74019eccf8..20ffe4a6ef 100644 --- a/lib/RedisRemoteSaiInterface.h +++ b/lib/RedisRemoteSaiInterface.h @@ -231,6 +231,15 @@ namespace sairedis _In_ sai_object_id_t switchId, _In_ const sai_attribute_t* attr); + /** + * @brief Checks whether attribute is custom SAI_REDIS_PORT attribute. + * + * This function should only be used on port_api set function. + */ + static bool isRedisPortAttribute( + _In_ sai_object_id_t obejctType, + _In_ const sai_attribute_t* attr); + void setMeta( _In_ std::weak_ptr meta); @@ -401,6 +410,11 @@ namespace sairedis _In_ sai_object_id_t objectId, _In_ const sai_attribute_t *attr); + sai_status_t setRedisPortExtensionAttribute( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr); + bool isSaiS8ListValidString( _In_ const sai_s8_list_t &s8list); @@ -428,6 +442,11 @@ namespace sairedis _In_ sai_object_id_t switchId, _In_ const sai_attribute_t *attr); + sai_status_t setLinkEventDampingConfig( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const std::vector &values); + void clear_local_state(); sai_switch_notifications_t processNotification( diff --git a/lib/Sai.cpp b/lib/Sai.cpp index 22b33e4764..03f441e560 100644 --- a/lib/Sai.cpp +++ b/lib/Sai.cpp @@ -251,6 +251,12 @@ sai_status_t Sai::set( REDIS_CHECK_CONTEXT(objectId); + if (RedisRemoteSaiInterface::isRedisPortAttribute(objectType, attr)) + { + // skip metadata if attribute is redis extension port attribute. + return context->m_redisSai->set(objectType, objectId, attr); + } + return context->m_meta->set(objectType, objectId, attr); } diff --git a/lib/sairediscommon.h b/lib/sairediscommon.h index 4594cab5b0..c2da2a08e5 100644 --- a/lib/sairediscommon.h +++ b/lib/sairediscommon.h @@ -52,6 +52,8 @@ #define REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_QUERY "object_type_get_availability_query" #define REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_RESPONSE "object_type_get_availability_response" +#define REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET "link_event_damping_config_set" + #define REDIS_FLEX_COUNTER_COMMAND_START_POLL "start_poll" #define REDIS_FLEX_COUNTER_COMMAND_STOP_POLL "stop_poll" #define REDIS_FLEX_COUNTER_COMMAND_SET_GROUP "set_counter_group" diff --git a/syncd/NotificationProcessor.cpp b/syncd/NotificationProcessor.cpp index 1bd5ce0b97..20eb3937cb 100644 --- a/syncd/NotificationProcessor.cpp +++ b/syncd/NotificationProcessor.cpp @@ -18,8 +18,10 @@ using namespace saimeta; NotificationProcessor::NotificationProcessor( _In_ std::shared_ptr producer, _In_ std::shared_ptr client, - _In_ std::function synchronizer): + _In_ std::function synchronizer, + _In_ std::function linkEventDampingApplier): m_synchronizer(synchronizer), + m_linkEventDampingApplier(linkEventDampingApplier), m_client(client), m_notifications(producer) { @@ -494,6 +496,9 @@ void NotificationProcessor::process_on_port_state_change( SWSS_LOG_DEBUG("port notification count: %u", count); + // Vector to store filtered notifications (after damping applied) + std::vector filtered_notifications; + for (uint32_t i = 0; i < count; i++) { sai_port_oper_status_notification_t *oper_stat = &data[i]; @@ -520,14 +525,43 @@ void NotificationProcessor::process_on_port_state_change( * Port may be in process of removal. OA may receive notification for VID either * SAI_NULL_OBJECT_ID or non exist at time of processing */ + SWSS_LOG_INFO("Port VID %s state change notification: %s", + sai_serialize_object_id(oper_stat->port_id).c_str(), + sai_serialize_port_oper_status(oper_stat->port_state).c_str()); - SWSS_LOG_INFO("Port VID %s state change notification", - sai_serialize_object_id(oper_stat->port_id).c_str()); - } + // Apply link event damping if configured + bool should_suppress = false; + if (m_linkEventDampingApplier != nullptr && oper_stat->port_id != SAI_NULL_OBJECT_ID) + { + should_suppress = m_linkEventDampingApplier(oper_stat->port_id, oper_stat->port_state); + } - std::string s = sai_serialize_port_oper_status_ntf(count, data); + if (!should_suppress) + { + // Add to filtered notifications + filtered_notifications.push_back(*oper_stat); + SWSS_LOG_INFO("Port state change PROPAGATED: %s -> %s", + sai_serialize_object_id(oper_stat->port_id).c_str(), + sai_serialize_port_oper_status(oper_stat->port_state).c_str()); + } + else + { + SWSS_LOG_INFO("Port state change SUPPRESSED by damping: %s -> %s", + sai_serialize_object_id(oper_stat->port_id).c_str(), + sai_serialize_port_oper_status(oper_stat->port_state).c_str()); + } + } - sendNotification(SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE, s); + // Send only non-suppressed (filtered) notifications + if (!filtered_notifications.empty()) + { + std::string s = sai_serialize_port_oper_status_ntf((uint32_t)filtered_notifications.size(), filtered_notifications.data()); + sendNotification(SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE, s); + } + else + { + SWSS_LOG_DEBUG("All port state changes were suppressed by damping, no notification sent"); + } } void NotificationProcessor::process_on_bfd_session_state_change( diff --git a/syncd/NotificationProcessor.h b/syncd/NotificationProcessor.h index 3ee4a941cf..4d17748663 100644 --- a/syncd/NotificationProcessor.h +++ b/syncd/NotificationProcessor.h @@ -22,7 +22,8 @@ namespace syncd NotificationProcessor( _In_ std::shared_ptr producer, _In_ std::shared_ptr client, - _In_ std::function synchronizer); + _In_ std::function synchronizer, + _In_ std::function linkEventDampingApplier = nullptr); virtual ~NotificationProcessor(); @@ -211,6 +212,12 @@ namespace syncd std::function m_synchronizer; + /** + * @brief Callback function to apply link event damping to port state changes + * Returns true if notification should be suppressed, false if it should be propagated + */ + std::function m_linkEventDampingApplier; + std::shared_ptr m_client; std::shared_ptr m_notifications; diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index d2bc0ba056..1af264b611 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -67,7 +67,8 @@ Syncd::Syncd( m_vendorSai(vendorSai), m_veryFirstRun(false), m_enableSyncMode(false), - m_timerWatchdog(cmd->m_watchdogWarnTimeSpan * WD_DELAY_FACTOR) + m_timerWatchdog(cmd->m_watchdogWarnTimeSpan * WD_DELAY_FACTOR), + m_runDampingTimerThread(false) { SWSS_LOG_ENTER(); @@ -134,6 +135,8 @@ Syncd::Syncd( // we need STATE_DB ASIC_DB and COUNTERS_DB m_dbAsic = std::make_shared(m_contextConfig->m_dbAsic, 0); + m_dbState = std::make_shared("STATE_DB", 0); + m_dampingCounterTable = std::make_shared(m_dbState.get(), "LINK_EVENT_DAMPING_STATS"); m_mdioIpcServer = std::make_shared(m_vendorSai, m_commandLineOptions->m_globalContext); if (m_contextConfig->m_zmqEnable) @@ -179,7 +182,11 @@ Syncd::Syncd( m_client = std::make_shared(m_dbAsic); } - m_processor = std::make_shared(m_notifications, m_client, std::bind(&Syncd::syncProcessNotification, this, _1)); + m_processor = std::make_shared( + m_notifications, + m_client, + std::bind(&Syncd::syncProcessNotification, this, _1), + std::bind(&Syncd::applyLinkEventDamping, this, _1, _2)); m_handler = std::make_shared(m_processor); m_sn.onFdbEvent = std::bind(&NotificationHandler::onFdbEvent, m_handler.get(), _1, _2); @@ -263,6 +270,9 @@ Syncd::Syncd( m_breakConfig = BreakConfigParser::parseBreakConfig(m_commandLineOptions->m_breakConfig); + // Start the damping timer thread for proactive timeout enforcement + startDampingTimerThread(); + SWSS_LOG_NOTICE("syncd started"); } @@ -270,7 +280,8 @@ Syncd::~Syncd() { SWSS_LOG_ENTER(); - // empty + // Stop the damping timer thread + stopDampingTimerThread(); } void Syncd::performStartupLogic() @@ -473,6 +484,9 @@ sai_status_t Syncd::processSingleEvent( if (op == REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_QUERY) return processObjectTypeGetAvailabilityQuery(kco); + if (op == REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET) + return processLinkEventDampingConfigSet(kco); + if (op == REDIS_FLEX_COUNTER_COMMAND_START_POLL) return processFlexCounterEvent(key, SET_COMMAND, kfvFieldsValues(kco)); @@ -842,6 +856,812 @@ sai_status_t Syncd::processStatsStCapabilityQuery( return status; } +sai_status_t Syncd::processLinkEventDampingConfigSet( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + auto& key = kfvKey(kco); + auto& values = kfvFieldsValues(kco); + + // Parse the key format: "OBJECT_TYPE:OBJECT_ID" + size_t colon_pos = key.find(":"); + if (colon_pos == std::string::npos) + { + SWSS_LOG_ERROR("invalid key format: %s", key.c_str()); + sendLinkEventDampingConfigResponse(SAI_STATUS_INVALID_PARAMETER); + return SAI_STATUS_INVALID_PARAMETER; + } + + // Extract object type and object ID + std::string strObjectType = key.substr(0, colon_pos); + std::string strObjectId = key.substr(colon_pos + 1); + + sai_object_type_t objectType; + sai_deserialize_object_type(strObjectType, objectType); + + // Link event damping is a software-based feature - validate port exists + if (objectType != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_ERROR("invalid object type for link event damping config: %s", + strObjectType.c_str()); + sendLinkEventDampingConfigResponse(SAI_STATUS_INVALID_PARAMETER); + return SAI_STATUS_INVALID_PARAMETER; + } + + sai_object_id_t portVid; + sai_deserialize_object_id(strObjectId, portVid); + + // Validate that the port exists by translating VID to RID + sai_object_id_t portRid = m_translator->translateVidToRid(portVid); + + if (portRid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("failed to translate port VID to RID"); + sendLinkEventDampingConfigResponse(SAI_STATUS_INVALID_PARAMETER); + return SAI_STATUS_INVALID_PARAMETER; + } + + // Link event damping is a software-based feature implemented in syncd. + // Store the configuration parameters on the port object so that + // OnPortStateChange can apply the damping algorithm before forwarding notifications. + // The damping parameters will be used to decide whether to suppress link state changes. + sai_status_t status = SAI_STATUS_SUCCESS; + + // Acquire lock to protect damping state + std::lock_guard lock(m_linkEventDampingMutex); + + // Get or create damping state for this port + auto& dampingState = m_portLinkEventDampingStates[portVid]; + + // Process each attribute and apply it to the port + for (const auto& v : values) + { + std::string strAttrId = fvField(v); + std::string strAttrValue = fvValue(v); + + SWSS_LOG_DEBUG("processing link event damping attribute: %s = %s", + strAttrId.c_str(), strAttrValue.c_str()); + + // Deserialize attribute ID + sai_redis_port_attr_t attrId; + sai_deserialize_redis_port_attr_id(strAttrId, attrId); + + // Parse and set the attribute value based on the attribute ID + switch (attrId) + { + case SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM: + { + sai_redis_link_event_damping_algorithm_t algo; + sai_deserialize_redis_link_event_damping_algorithm(strAttrValue, algo); + + SWSS_LOG_INFO("setting link event damping algorithm on port %s: %d", + strObjectId.c_str(), algo); + + // Link event damping is a software-only feature as of now + // Store the configuration locally for use in notification + // processing. + dampingState.algorithm = algo; + + status = SAI_STATUS_SUCCESS; + break; + } + + case SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG: + { + // Allocate temporary memory for the config structure + sai_redis_link_event_damping_algo_aied_config_t *config = + new sai_redis_link_event_damping_algo_aied_config_t(); + + sai_deserialize_redis_link_event_damping_aied_config(strAttrValue, *config); + + SWSS_LOG_INFO("setting link event damping AIED config on port %s: " + "max_suppress_time=%u, suppress_threshold=%u, " + "reuse_threshold=%u, decay_half_life=%u, flap_penalty=%u", + strObjectId.c_str(), config->max_suppress_time, + config->suppress_threshold, config->reuse_threshold, + config->decay_half_life, config->flap_penalty); + + // Link event damping is a software-only feature as of now + // Store the configuration locally for use in notification + // processing. + dampingState.aied_config = *config; + + // Free the temporary allocated memory + delete config; + + status = SAI_STATUS_SUCCESS; + break; + } + + default: + { + SWSS_LOG_WARN("unknown attribute ID: %d for link event damping", attrId); + status = SAI_STATUS_INVALID_PARAMETER; + break; + } + } + + if (status != SAI_STATUS_SUCCESS && status != SAI_STATUS_INVALID_PARAMETER) + { + // Log error but continue processing other attributes + SWSS_LOG_WARN("error processing link event damping attribute %s: %s", + strAttrId.c_str(), sai_serialize_status(status).c_str()); + break; + } + } + + sendLinkEventDampingConfigResponse(status); + + return status; +} + +void Syncd::sendLinkEventDampingConfigResponse( + _In_ sai_status_t status) +{ + SWSS_LOG_ENTER(); + + // If sync mode is not enabled, do not send response. + if (!m_enableSyncMode) + { + return; + } + + std::string strStatus = sai_serialize_status(status); + + std::vector entry; + + SWSS_LOG_INFO("sending link event damping config response: %s", strStatus.c_str()); + + m_selectableChannel->set(strStatus, entry, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); +} + +uint64_t Syncd::getCurrentTimeMs() +{ + auto now = std::chrono::system_clock::now(); + auto duration = now.time_since_epoch(); + return std::chrono::duration_cast(duration).count(); +} + +void Syncd::decayPenalty( + _In_ LinkEventDampingPortState& state, + _In_ uint64_t currentTimeMs) +{ + SWSS_LOG_ENTER(); + + if (state.current_penalty == 0) + { + return; // No penalty to decay + } + + if (state.aied_config.decay_half_life == 0) + { + return; // Invalid configuration, skip decay + } + + // Use last_decay_time to track decay independently from state transitions + // This ensures penalty decays even if no link state changes occur + uint64_t base_time = state.last_decay_time_ms; + if (base_time == 0) + { + // First time calculating decay - use last transition time as base + base_time = state.last_transition_time_ms; + } + + // Calculate elapsed time in milliseconds since last decay + uint64_t elapsed_ms = currentTimeMs - base_time; + + if (elapsed_ms <= 0) + { + return; // No time has elapsed + } + + // Penalty decay formula: P(t) = P0 * (0.5 ^ (t / half_life)) + // We use floating point for the calculation + double half_lives = (double)elapsed_ms / state.aied_config.decay_half_life; + double decay_factor = std::pow(0.5, half_lives); + uint32_t decayed_penalty = (uint32_t)(state.current_penalty * decay_factor); + + // Ensure penalty doesn't go below 0 + if (decayed_penalty < state.current_penalty) + { + state.current_penalty = decayed_penalty; + state.last_decay_time_ms = currentTimeMs; // Update last decay time + SWSS_LOG_DEBUG("Port penalty decayed: %u (half_life=%u ms, elapsed=%lu ms, decay_factor=%f)", + state.current_penalty, state.aied_config.decay_half_life, elapsed_ms, decay_factor); + } + else if (state.last_decay_time_ms == 0) + { + // Initialize decay time on first check + state.last_decay_time_ms = currentTimeMs; + } +} + +bool Syncd::applyAiedAlgorithm( + _In_ sai_object_id_t portVid, + _In_ LinkEventDampingPortState& state, + _In_ sai_port_oper_status_t newStatus, + _In_ uint64_t currentTimeMs) +{ + SWSS_LOG_ENTER(); + + std::string portVidStr = sai_serialize_object_id(portVid); + + // Validate configuration + if (state.aied_config.decay_half_life > state.aied_config.max_suppress_time) + { + SWSS_LOG_WARN("Port VID %s invalid damping configuration: " + "decay_half_life (%u ms) > max_suppress_time (%u ms). Damping disabled.", + portVidStr.c_str(), state.aied_config.decay_half_life, + state.aied_config.max_suppress_time); + return false; // Damping disabled for invalid config + } + + // First, apply penalty decay + decayPenalty(state, currentTimeMs); + + // Track if damping was active before this event + bool was_damping_active_before = state.is_damping_active; + + // Check if a link state change occurred + if (state.physical_status != newStatus) + { + // Link state transitioned + state.pre_damping_link_transitions++; + + if (newStatus == SAI_PORT_OPER_STATUS_UP) + { + state.pre_damping_up_events++; + } + else if (newStatus == SAI_PORT_OPER_STATUS_DOWN) + { + state.pre_damping_down_events++; + // Reset damping timer on DOWN event if damping is already active + if (state.is_damping_active) + { + state.damping_start_time_ms = currentTimeMs; + SWSS_LOG_DEBUG("Damping timer reset on DOWN event: new start time = %lu ms", + currentTimeMs); + } + } + + // Add penalty ONLY on DOWN events (UP -> DOWN) + if (state.physical_status == SAI_PORT_OPER_STATUS_UP && newStatus == SAI_PORT_OPER_STATUS_DOWN) + { + state.current_penalty += state.aied_config.flap_penalty; + + // Calculate penalty ceiling: 2^(max_suppress_time/decay_half_life) * reuse_threshold + double exponent = (double)state.aied_config.max_suppress_time / state.aied_config.decay_half_life; + uint32_t penalty_ceiling = (uint32_t)(std::pow(2.0, exponent) * state.aied_config.reuse_threshold); + + if (state.current_penalty > penalty_ceiling) + { + state.current_penalty = penalty_ceiling; + } + + SWSS_LOG_DEBUG("Port DOWN event: penalty accumulated to %u " + "(penalty_ceiling: %u, flap_penalty: %u)", + state.current_penalty, penalty_ceiling, state.aied_config.flap_penalty); + } + else + { + SWSS_LOG_DEBUG("Port UP event: no penalty added (penalty remains: %u)", + state.current_penalty); + } + + // Update physical status and timestamps + state.physical_status = newStatus; + state.last_transition_time_ms = currentTimeMs; + + // If this is the first state change after damping config was set, + // initialize decay time as well + if (state.last_decay_time_ms == 0) + { + state.last_decay_time_ms = currentTimeMs; + } + + // Check if we should enter damping state + if (state.current_penalty >= state.aied_config.suppress_threshold && + !state.is_damping_active) + { + std::string portVidStr = sai_serialize_object_id(portVid); + SWSS_LOG_NOTICE("Port VID %s entering damped state: penalty (%u) >= " + "suppress_threshold (%u) at time %lu ms. Current event will be " + "PROPAGATED, future events will be suppressed.", + portVidStr.c_str(), state.current_penalty, + state.aied_config.suppress_threshold, currentTimeMs); + state.is_damping_active = true; + state.damping_start_time_ms = currentTimeMs; + } + + // Write updated pre-damping counters and physical status to STATE_DB + writeDampingCountersToStateDb(portVid, state); + } + + // Damping exits when EITHER: + // 1. Time-based: damping_duration_ms >= max_suppress_time + // 2. Penalty-based: current_penalty < reuse_threshold (decay-based recovery) + if (state.is_damping_active) + { + // Check timeout - never suppress longer than max_suppress_time + // This is a hard timestamp-based limit to prevent infinite suppression + uint64_t damping_duration_ms = currentTimeMs - state.damping_start_time_ms; + + if (damping_duration_ms >= state.aied_config.max_suppress_time) + { + // Store temporary strings to avoid dangling pointers + std::string physicalStatusStr = sai_serialize_port_oper_status(state.physical_status); + std::string advertisedStatusStr = sai_serialize_port_oper_status(state.advertised_status); + SWSS_LOG_NOTICE("Port VID %s exiting damped state: max suppress time (%u ms) " + "exceeded. Duration: %lu ms. Physical state: %s, Advertised state: %s", + portVidStr.c_str(), state.aied_config.max_suppress_time, + damping_duration_ms, physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + state.is_damping_active = false; + state.damping_start_time_ms = 0; // Reset timer when exiting damping + + // Propagate last link event when penalty decays below reuse threshold + if (state.advertised_status != state.physical_status) + { + state.pending_state_sync = true; + SWSS_LOG_NOTICE("Port VID %s state mismatch detected on damping " + "exit (timeout): physical=%s, advertised=%s. " + "Marking for state sync on next notification.", + portVidStr.c_str(), physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + } + state.advertised_status = state.physical_status; + + // Write updated state to STATE_DB after exiting damping + writeDampingCountersToStateDb(portVid, state); + } + // Check reuse threshold - exit if penalty decays below threshold + // Penalty decays based on last_decay_time tracking + else if (state.current_penalty < state.aied_config.reuse_threshold) + { + // Store temporary strings to avoid dangling pointers + std::string physicalStatusStr = sai_serialize_port_oper_status(state.physical_status); + std::string advertisedStatusStr = sai_serialize_port_oper_status(state.advertised_status); + SWSS_LOG_NOTICE("Port VID %s exiting damped state: penalty (%u) < " + "reuse_threshold (%u). Penalty decayed due to exponential decay " + "formula. Physical state: %s, Advertised state: %s", + portVidStr.c_str(), state.current_penalty, + state.aied_config.reuse_threshold, physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + state.is_damping_active = false; + state.damping_start_time_ms = 0; // Reset timer when exiting damping + + // Propagate last link event when penalty decays below reuse threshold + if (state.advertised_status != state.physical_status) + { + state.pending_state_sync = true; + SWSS_LOG_NOTICE("Port VID %s state mismatch detected on damping " + "exit (decay): physical=%s, advertised=%s. " + "Marking for state sync on next notification.", + portVidStr.c_str(), physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + } + state.advertised_status = state.physical_status; + + // Write updated state to STATE_DB after exiting damping + writeDampingCountersToStateDb(portVid, state); + } + } + + // Determine if notification should be suppressed + bool should_suppress = false; + + // Only suppress when damping is active AND this is NOT the threshold-crossing event + // The threshold-crossing event itself should be propagated + if (state.is_damping_active && was_damping_active_before) + { + // Calculate current suppression time based on damping algorithm + uint64_t damping_duration_ms = currentTimeMs - state.damping_start_time_ms; + + if (damping_duration_ms < state.aied_config.max_suppress_time) + { + // Calculate expected suppression time + // suppression_time = decay_half_life * log2(reuse_threshold / accumulated_penalty) + if (state.current_penalty > 0 && state.current_penalty >= state.aied_config.reuse_threshold) + { + double suppression_ratio = (double)state.aied_config.reuse_threshold / state.current_penalty; + double expected_suppress_time = state.aied_config.decay_half_life * std::log2(suppression_ratio); + + SWSS_LOG_DEBUG("Port damping active: suppression_time=%.0f ms, " + "max_suppress_time=%u ms, current_penalty=%u, reuse_threshold=%u", + expected_suppress_time, state.aied_config.max_suppress_time, + state.current_penalty, state.aied_config.reuse_threshold); + } + + // Suppress the notification + should_suppress = true; + state.last_suppressed_status = newStatus; // Track what was suppressed + // Store temporary strings to avoid dangling pointers + std::string newStatusStr = sai_serialize_port_oper_status(newStatus); + SWSS_LOG_NOTICE("Port VID %s suppressing port state change notification: " + "new_status=%s (damping active, penalty: %u, duration: %lu ms)", + portVidStr.c_str(), newStatusStr.c_str(), + state.current_penalty, damping_duration_ms); + } + else + { + // Should not happen due to exit check above, but handle gracefully + should_suppress = false; + + SWSS_LOG_WARN("Port VID %s unexpected state: damping_active=true but " + "duration >= max_suppress_time", portVidStr.c_str()); + } + } + else + { + // Damping is NOT active OR this is the threshold-crossing event - propagate the notification + should_suppress = false; + std::string physicalStatusStr = sai_serialize_port_oper_status(state.physical_status); + std::string advertisedStatusStr = sai_serialize_port_oper_status(state.advertised_status); + + // Track advertised transitions + if (newStatus == SAI_PORT_OPER_STATUS_UP) + { + state.post_damping_up_events++; + } + else if (newStatus == SAI_PORT_OPER_STATUS_DOWN) + { + state.post_damping_down_events++; + } + state.post_damping_link_transitions++; + + // SYNC STATE: Update advertised to match new state + state.advertised_status = newStatus; + + // Clear the pending sync flag since state is now synchronized + if (state.pending_state_sync && newStatus == state.physical_status) + { + state.pending_state_sync = false; + SWSS_LOG_INFO("Port state sync completed: physical=%s, advertised=%s", + physicalStatusStr.c_str(), advertisedStatusStr.c_str()); + } + + // Write updated counters to STATE_DB + writeDampingCountersToStateDb(portVid, state); + + // When damping changes state (enabled->disabled), propagate the event + if (was_damping_active_before && !state.is_damping_active) + { + SWSS_LOG_NOTICE("Port VID %s state change PROPAGATED (damping state changed " + "from active to inactive): physical=%s, advertised=%s", + portVidStr.c_str(), physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + } + else + { + SWSS_LOG_INFO("Port VID %s state change PROPAGATED: physical=%s, advertised=%s (damping inactive)", + portVidStr.c_str(), physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + } + + } + + return should_suppress; +} + +bool Syncd::applyLinkEventDamping( + _In_ sai_object_id_t portVid, + _In_ sai_port_oper_status_t newStatus) +{ + SWSS_LOG_ENTER(); + + std::lock_guard lock(m_linkEventDampingMutex); + + // Check if damping is configured for this port + auto it = m_portLinkEventDampingStates.find(portVid); + if (it == m_portLinkEventDampingStates.end()) + { + // No damping configured for this port + return false; // Don't suppress + } + + LinkEventDampingPortState& state = it->second; + + // Check if damping algorithm is enabled + if (state.algorithm == SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED) + { + return false; // Damping disabled + } + + uint64_t currentTimeMs = getCurrentTimeMs(); + + // Apply the appropriate damping algorithm + switch (state.algorithm) + { + case SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED: + return applyAiedAlgorithm(portVid, state, newStatus, currentTimeMs); + + default: + SWSS_LOG_WARN("Unknown damping algorithm: %d", state.algorithm); + return false; + } +} + +void Syncd::checkDampedPortsTimeout() +{ + SWSS_LOG_ENTER(); + + std::lock_guard lock(m_linkEventDampingMutex); + + uint64_t currentTimeMs = getCurrentTimeMs(); + std::vector> portsToSync; + + // Iterate through all ports with damping configured + for (auto& kv : m_portLinkEventDampingStates) + { + auto& portVid = kv.first; + auto& state = kv.second; + + // Only check ports that are currently in damped state + if (!state.is_damping_active) + { + continue; + } + + // Check if damping algorithm is enabled + if (state.algorithm != SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED) + { + continue; + } + + std::string portVidStr = sai_serialize_object_id(portVid); + std::string physicalStatusStr = sai_serialize_port_oper_status(state.physical_status); + std::string advertisedStatusStr = sai_serialize_port_oper_status(state.advertised_status); + // Apply penalty decay first - penalty naturally decays over time + decayPenalty(state, currentTimeMs); + + // Check if penalty has decayed below reuse threshold + if (state.current_penalty < state.aied_config.reuse_threshold) + { + SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s exiting damped state: " + "penalty (%u) < reuse_threshold (%u). Penalty decayed due to " + "exponential decay formula. Physical state: %s, Advertised state: %s", + portVidStr.c_str(), state.current_penalty, + state.aied_config.reuse_threshold, physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + + // Exit damping state + state.is_damping_active = false; + state.damping_start_time_ms = 0; // Reset timer when exiting damping + + // Check if there's a state mismatch that needs to be propagated + if (state.advertised_status != state.physical_status) + { + SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s state mismatch " + "detected on damping exit (decay): " + "physical=%s, advertised=%s. Will send notification.", + portVidStr.c_str(), physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + + // Update advertised status to match physical + state.advertised_status = state.physical_status; + state.pending_state_sync = false; + + // Collect port info for notification + portsToSync.push_back(std::make_pair(portVid, state.physical_status)); + } + else + { + SWSS_LOG_INFO("Proactive timeout check: Port VID %s exited damping " + "with no state mismatch.", portVidStr.c_str()); + } + + // Write updated state to STATE_DB after exiting damping + writeDampingCountersToStateDb(portVid, state); + + // Skip to next port since we've already handled this one + continue; + } + + // Calculate how long the port has been damped + uint64_t damping_duration_ms = currentTimeMs - state.damping_start_time_ms; + + // Check if max_suppress_time has been exceeded + if (damping_duration_ms >= state.aied_config.max_suppress_time) + { + SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s exiting damped " + "state: max suppress time (%u ms) exceeded. " + "Duration: %lu ms. Physical state: %s, Advertised state: %s", + portVidStr.c_str(), state.aied_config.max_suppress_time, + damping_duration_ms, physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + + // Exit damping state + state.is_damping_active = false; + state.damping_start_time_ms = 0; // Reset timer when exiting damping + + // Check if there's a state mismatch that needs to be propagated + if (state.advertised_status != state.physical_status) + { + SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s state mismatch detected on damping exit: " + "physical=%s, advertised=%s. Will send notification.", + portVidStr.c_str(), physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + + // Update advertised status to match physical + state.advertised_status = state.physical_status; + state.pending_state_sync = false; + + // Collect port info for notification + portsToSync.push_back(std::make_pair(portVid, state.physical_status)); + } + else + { + SWSS_LOG_INFO("Proactive timeout check: Port VID %s exited damping " + "with no state mismatch.", portVidStr.c_str()); + } + + // Write updated state to STATE_DB after exiting damping + writeDampingCountersToStateDb(portVid, state); + } + else + { + // Port is still in damped state - write updated stats to STATE_DB + // to reflect the decayed penalty value in real-time + writeDampingCountersToStateDb(portVid, state); + + SWSS_LOG_DEBUG("Proactive timeout check: Port VID %s still damped: " + "penalty=%u, duration=%lu ms", portVidStr.c_str(), + state.current_penalty, damping_duration_ms); + } + } + + // Release the lock before sending notifications + // Note: We make a copy of the port list above to avoid holding the lock during notification send + // Send notifications for ports that need state synchronization + if (!portsToSync.empty()) + { + SWSS_LOG_NOTICE("Proactive timeout check: Sending %zu port state notifications " + "after damping timeout", portsToSync.size()); + + // Send each port notification through the notification system + for (const auto& kv : portsToSync) + { + const auto& portVid = kv.first; + const auto& status = kv.second; + + std::string portVidStr = sai_serialize_object_id(portVid); + std::string statusStr = sai_serialize_port_oper_status(status); + + // Build notification data + sai_port_oper_status_notification_t notification; + notification.port_id = portVid; + notification.port_state = status; + + std::string serialized = sai_serialize_port_oper_status_ntf(1, ¬ification); + + // Send directly through the notification producer + std::vector entry; + m_notifications->send(SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE, + serialized, entry); + SWSS_LOG_NOTICE("Proactive timeout check: Sent notification for Port VID %s -> %s", + portVidStr.c_str(), statusStr.c_str()); + } + } +} + +void Syncd::dampingTimerThreadFunc() +{ + SWSS_LOG_ENTER(); + SWSS_LOG_NOTICE("Damping timer thread started"); + + while (true) + { + { + std::unique_lock lock(m_dampingTimerMutex); + + // Wait for 1 second or until signaled to stop + if (m_dampingTimerCv.wait_for(lock, std::chrono::seconds(1), [this] { return !m_runDampingTimerThread; })) + { + // Signaled to stop + SWSS_LOG_NOTICE("Damping timer thread received stop signal"); + break; + } + + // Check if still running (in case of spurious wakeup) + if (!m_runDampingTimerThread) + { + break; + } + } + + // Perform the proactive timeout check + try + { + checkDampedPortsTimeout(); + } + catch (const std::exception& e) + { + SWSS_LOG_ERROR("Exception in damping timer thread: %s", e.what()); + } + catch (...) + { + SWSS_LOG_ERROR("Unknown exception in damping timer thread"); + } + } + + SWSS_LOG_NOTICE("Damping timer thread stopped"); +} + +void Syncd::startDampingTimerThread() +{ + SWSS_LOG_ENTER(); + + if (m_runDampingTimerThread) + { + SWSS_LOG_WARN("Damping timer thread already running"); + return; + } + + m_runDampingTimerThread = true; + m_dampingTimerThread = std::make_shared(&Syncd::dampingTimerThreadFunc, this); + + SWSS_LOG_NOTICE("Started damping timer thread for proactive max_suppress_time enforcement"); +} + +void Syncd::stopDampingTimerThread() +{ + SWSS_LOG_ENTER(); + + if (!m_runDampingTimerThread) + { + SWSS_LOG_INFO("Damping timer thread not running"); + return; + } + + // Signal the thread to stop + { + std::lock_guard lock(m_dampingTimerMutex); + m_runDampingTimerThread = false; + } + m_dampingTimerCv.notify_one(); + + // Wait for the thread to finish + if (m_dampingTimerThread && m_dampingTimerThread->joinable()) + { + m_dampingTimerThread->join(); + SWSS_LOG_NOTICE("Damping timer thread stopped and joined"); + } + + m_dampingTimerThread.reset(); +} + +void Syncd::writeDampingCountersToStateDb( + _In_ sai_object_id_t portVid, + _In_ const LinkEventDampingPortState& state) +{ + SWSS_LOG_ENTER(); + + // Convert VID to string for STATE_DB key + std::string portVidStr = sai_serialize_object_id(portVid); + + // Prepare counter fields + std::vector fields; + fields.emplace_back("pre_damping_link_transitions", std::to_string(state.pre_damping_link_transitions)); + fields.emplace_back("pre_damping_up_events", std::to_string(state.pre_damping_up_events)); + fields.emplace_back("pre_damping_down_events", std::to_string(state.pre_damping_down_events)); + fields.emplace_back("post_damping_up_events", std::to_string(state.post_damping_up_events)); + fields.emplace_back("post_damping_down_events", std::to_string(state.post_damping_down_events)); + fields.emplace_back("post_damping_link_transitions", std::to_string(state.post_damping_link_transitions)); + + // Add damping state information + fields.emplace_back("is_damping_active", state.is_damping_active ? "true" : "false"); + fields.emplace_back("current_penalty", std::to_string(state.current_penalty)); + fields.emplace_back("damping_start_time_ms", std::to_string(state.damping_start_time_ms)); + fields.emplace_back("physical_status", sai_serialize_port_oper_status(state.physical_status)); + fields.emplace_back("advertised_status", sai_serialize_port_oper_status(state.advertised_status)); + + // Write to STATE_DB + m_dampingCounterTable->set(portVidStr, fields); + + SWSS_LOG_DEBUG("Wrote damping counters to STATE_DB for port %s", portVidStr.c_str()); +} + sai_status_t Syncd::processFdbFlush( _In_ const swss::KeyOpFieldsValuesTuple &kco) { diff --git a/syncd/Syncd.cpp.orig b/syncd/Syncd.cpp.orig new file mode 100644 index 0000000000..d2bc0ba056 --- /dev/null +++ b/syncd/Syncd.cpp.orig @@ -0,0 +1,6105 @@ +#include "Syncd.h" +#include "VidManager.h" +#include "NotificationHandler.h" +#include "Workaround.h" +#include "ComparisonLogic.h" +#include "HardReiniter.h" +#include "RedisClient.h" +#include "DisabledRedisClient.h" +#include "RequestShutdown.h" +#include "WarmRestartTable.h" +#include "ContextConfigContainer.h" +#include "BreakConfigParser.h" +#include "RedisNotificationProducer.h" +#include "ZeroMQNotificationProducer.h" +#include "WatchdogScope.h" +#include "VendorSaiOptions.h" + +#include "sairediscommon.h" + +#include "swss/logger.h" +#include "swss/select.h" +#include "swss/tokenize.h" +#include "swss/notificationproducer.h" +#include "swss/exec.h" +#include "swss/dbconnector.h" +#include "swss/table.h" + +#include "meta/sai_serialize.h" +#include "meta/ZeroMQSelectableChannel.h" +#include "meta/RedisSelectableChannel.h" +#include "meta/PerformanceIntervalTimer.h" +#include "meta/Globals.h" + +#include "vslib/saivs.h" + +#include "config.h" + +#include +#include + +#include +#include + +#define DEF_SAI_WARM_BOOT_DATA_FILE "/var/warmboot/sai-warmboot.bin" +#define SAI_FAILURE_DUMP_SCRIPT "/usr/bin/sai_failure_dump.sh" +#define SYNCD_ZMQ_RESPONSE_BUFFER_SIZE (128*1024*1024) + +using namespace syncd; +using namespace saimeta; +using namespace sairediscommon; +using namespace std::placeholders; + +#ifdef ASAN_ENABLED +#define WD_DELAY_FACTOR 2 +#else +#define WD_DELAY_FACTOR 1 +#endif + +Syncd::Syncd( + _In_ std::shared_ptr vendorSai, + _In_ std::shared_ptr cmd, + _In_ bool isWarmStart): + m_commandLineOptions(cmd), + m_isWarmStart(isWarmStart), + m_firstInitWasPerformed(false), + m_asicInitViewMode(false), // by default we are in APPLY view mode + m_vendorSai(vendorSai), + m_veryFirstRun(false), + m_enableSyncMode(false), + m_timerWatchdog(cmd->m_watchdogWarnTimeSpan * WD_DELAY_FACTOR) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("sairedis git revision %s, SAI git revision: %s", SAIREDIS_GIT_REVISION, SAI_GIT_REVISION); + + SWSS_LOG_NOTICE("command line: %s", m_commandLineOptions->getCommandLineString().c_str()); + + auto ccc = sairedis::ContextConfigContainer::loadFromFile(m_commandLineOptions->m_contextConfig.c_str()); + + m_contextConfig = ccc->get(m_commandLineOptions->m_globalContext); + + if (m_contextConfig == nullptr) + { + SWSS_LOG_THROW("no context config defined at global context %u", m_commandLineOptions->m_globalContext); + } + + if (m_commandLineOptions->m_enableSyncMode + && !(m_contextConfig->m_loadedFromJson && m_contextConfig->m_zmqEnable)) + { + SWSS_LOG_WARN("enable sync mode is deprecated, please use communication mode, FORCING redis sync mode"); + + m_enableSyncMode = true; + + m_contextConfig->m_zmqEnable = false; + + m_commandLineOptions->m_redisCommunicationMode = SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC; + } + + if (m_commandLineOptions->m_redisCommunicationMode == SAI_REDIS_COMMUNICATION_MODE_ZMQ_SYNC) + { + // If context_config.json explicitly set zmq_enable=false, + // respect it and fall back to Redis sync + if (m_contextConfig->m_loadedFromJson && !m_contextConfig->m_zmqEnable) + { + SWSS_LOG_NOTICE("context %u: zmq_enable=false in context config, falling back to Redis sync", + m_contextConfig->m_guid); + + m_enableSyncMode = true; + + m_commandLineOptions->m_redisCommunicationMode = SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC; + } + else + { + SWSS_LOG_NOTICE("zmq sync mode enabled via cmd line for context %u", m_contextConfig->m_guid); + + m_contextConfig->m_zmqEnable = true; + + m_enableSyncMode = true; + } + } + + auto vso = std::make_shared(); + + vso->m_checkAttrVersion = m_commandLineOptions->m_enableAttrVersionCheck; + + m_vendorSai->setOptions(VendorSaiOptions::OPTIONS_KEY, vso); + + m_manager = std::make_shared(m_vendorSai, m_contextConfig->m_dbCounters, m_commandLineOptions->m_supportingBulkCounterGroups); + + loadProfileMap(); + + m_profileIter = m_profileMap.begin(); + + // we need STATE_DB ASIC_DB and COUNTERS_DB + + m_dbAsic = std::make_shared(m_contextConfig->m_dbAsic, 0); + m_mdioIpcServer = std::make_shared(m_vendorSai, m_commandLineOptions->m_globalContext); + + if (m_contextConfig->m_zmqEnable) + { + m_notifications = std::make_shared(m_contextConfig->m_zmqNtfEndpoint); + + SWSS_LOG_NOTICE("zmq enabled, forcing sync mode"); + + m_enableSyncMode = true; + + m_selectableChannel = std::make_shared(m_contextConfig->m_zmqEndpoint, SYNCD_ZMQ_RESPONSE_BUFFER_SIZE); + } + else + { + m_notifications = std::make_shared(m_contextConfig->m_dbAsic); + + m_enableSyncMode = m_commandLineOptions->m_redisCommunicationMode == SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC; + + bool modifyRedis = m_enableSyncMode ? false : true; + + m_selectableChannel = std::make_shared( + m_dbAsic, + ASIC_STATE_TABLE, + REDIS_TABLE_GETRESPONSE, + TEMP_PREFIX, + modifyRedis); + } + + bool isVirtualSwitch = m_profileMap.find(SAI_KEY_VS_SWITCH_TYPE) != m_profileMap.end(); + swss::DBConnector configDb("CONFIG_DB", 0); + swss::Table deviceMetadataTable(&configDb, "DEVICE_METADATA"); + std::string switchType; + deviceMetadataTable.hget("localhost", "switch_type", switchType); + + bool isDpuSwitch = switchType == "dpu"; + + if (m_contextConfig->m_zmqEnable && isDpuSwitch && !isVirtualSwitch) + { + m_client = std::make_shared(); + } + else + { + m_client = std::make_shared(m_dbAsic); + } + + m_processor = std::make_shared(m_notifications, m_client, std::bind(&Syncd::syncProcessNotification, this, _1)); + m_handler = std::make_shared(m_processor); + + m_sn.onFdbEvent = std::bind(&NotificationHandler::onFdbEvent, m_handler.get(), _1, _2); + m_sn.onNatEvent = std::bind(&NotificationHandler::onNatEvent, m_handler.get(), _1, _2); + m_sn.onPortStateChange = std::bind(&NotificationHandler::onPortStateChange, m_handler.get(), _1, _2); + m_sn.onQueuePfcDeadlock = std::bind(&NotificationHandler::onQueuePfcDeadlock, m_handler.get(), _1, _2); + m_sn.onSwitchAsicSdkHealthEvent = std::bind(&NotificationHandler::onSwitchAsicSdkHealthEvent, m_handler.get(), _1, _2, _3, _4, _5, _6); + m_sn.onSwitchShutdownRequest = std::bind(&NotificationHandler::onSwitchShutdownRequest, m_handler.get(), _1); + m_sn.onSwitchStateChange = std::bind(&NotificationHandler::onSwitchStateChange, m_handler.get(), _1, _2); + m_sn.onBfdSessionStateChange = std::bind(&NotificationHandler::onBfdSessionStateChange, m_handler.get(), _1, _2); + m_sn.onIcmpEchoSessionStateChange = std::bind(&NotificationHandler::onIcmpEchoSessionStateChange, m_handler.get(), _1, _2); + m_sn.onPortHostTxReady = std::bind(&NotificationHandler::onPortHostTxReady, m_handler.get(), _1, _2, _3); + m_sn.onTwampSessionEvent = std::bind(&NotificationHandler::onTwampSessionEvent, m_handler.get(), _1, _2); + m_sn.onTamTelTypeConfigChange = std::bind(&NotificationHandler::onTamTelTypeConfigChange, m_handler.get(), _1); + m_sn.onSwitchMacsecPostStatus = std::bind(&NotificationHandler::onSwitchMacsecPostStatus, m_handler.get(), _1, _2); + m_sn.onMacsecPostStatus = std::bind(&NotificationHandler::onMacsecPostStatus, m_handler.get(), _1, _2); + m_sn.onHaSetEvent = std::bind(&NotificationHandler::onHaSetEvent, m_handler.get(), _1, _2); + m_sn.onHaScopeEvent = std::bind(&NotificationHandler::onHaScopeEvent, m_handler.get(), _1, _2); + m_sn.onFlowBulkGetSessionEvent = std::bind(&NotificationHandler::onFlowBulkGetSessionEvent, m_handler.get(), _1, _2, _3); + + m_handler->setSwitchNotifications(m_sn.getSwitchNotifications()); + + m_restartQuery = std::make_shared(m_dbAsic.get(), SYNCD_NOTIFICATION_CHANNEL_RESTARTQUERY_PER_DB(m_contextConfig->m_dbAsic)); + + // TODO to be moved to ASIC_DB + m_dbFlexCounter = std::make_shared(m_contextConfig->m_dbFlex, 0); + m_flexCounter = std::make_shared(m_dbFlexCounter.get(), FLEX_COUNTER_TABLE); + m_flexCounterGroup = std::make_shared(m_dbFlexCounter.get(), FLEX_COUNTER_GROUP_TABLE); + m_flexCounterTable = std::make_shared(m_dbFlexCounter.get(), FLEX_COUNTER_TABLE); + m_flexCounterGroupTable = std::make_shared(m_dbFlexCounter.get(), FLEX_COUNTER_GROUP_TABLE); + + m_switchConfigContainer = std::make_shared(); + m_redisVidIndexGenerator = std::make_shared(m_dbAsic, REDIS_KEY_VIDCOUNTER); + + m_virtualObjectIdManager = + std::make_shared( + m_commandLineOptions->m_globalContext, + m_switchConfigContainer, + m_redisVidIndexGenerator); + + // TODO move to syncd object + m_translator = std::make_shared(m_client, m_virtualObjectIdManager, vendorSai); + + m_processor->m_translator = m_translator; // TODO as param + + m_veryFirstRun = isVeryFirstRun(); + + performStartupLogic(); + + m_smt.profileGetValue = std::bind(&Syncd::profileGetValue, this, _1, _2); + m_smt.profileGetNextValue = std::bind(&Syncd::profileGetNextValue, this, _1, _2, _3); + + m_test_services = m_smt.getServiceMethodTable(); + + sai_status_t status = vendorSai->apiInitialize(0, &m_test_services); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("FATAL: failed to sai_api_initialize: %s", + sai_serialize_status(status).c_str()); + + abort(); + } + + setSaiApiLogLevel(); + + sai_api_version_t apiVersion = SAI_VERSION(0,0,0); // invalid version + + status = m_vendorSai->queryApiVersion(&apiVersion); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("failed to obtain libsai api version: %s", sai_serialize_status(status).c_str()); + } + else + { + SWSS_LOG_NOTICE("libsai api version: %lu", apiVersion); + } + + m_handler->setApiVersion(apiVersion); + + m_breakConfig = BreakConfigParser::parseBreakConfig(m_commandLineOptions->m_breakConfig); + + SWSS_LOG_NOTICE("syncd started"); +} + +Syncd::~Syncd() +{ + SWSS_LOG_ENTER(); + + // empty +} + +void Syncd::performStartupLogic() +{ + SWSS_LOG_ENTER(); + // ignore warm logic here if syncd starts in fast-boot, express-boot or Mellanox fastfast boot mode + + if (m_isWarmStart && m_commandLineOptions->m_startType != SAI_START_TYPE_FASTFAST_BOOT && + m_commandLineOptions->m_startType != SAI_START_TYPE_EXPRESS_BOOT && + m_commandLineOptions->m_startType != SAI_START_TYPE_FAST_BOOT) + { + SWSS_LOG_WARN("override command line startType=%s via SAI_START_TYPE_WARM_BOOT", + CommandLineOptions::startTypeToString(m_commandLineOptions->m_startType).c_str()); + + m_commandLineOptions->m_startType = SAI_START_TYPE_WARM_BOOT; + } + + if (m_commandLineOptions->m_startType == SAI_START_TYPE_WARM_BOOT) + { + const char *warmBootReadFile = profileGetValue(0, SAI_KEY_WARM_BOOT_READ_FILE); + + SWSS_LOG_NOTICE("using warmBootReadFile: '%s'", warmBootReadFile); + + if (warmBootReadFile == NULL || access(warmBootReadFile, F_OK) == -1) + { + SWSS_LOG_WARN("user requested warmStart but warmBootReadFile is not specified or not accessible, forcing cold start"); + + m_commandLineOptions->m_startType = SAI_START_TYPE_COLD_BOOT; + } + } + + if (m_commandLineOptions->m_startType == SAI_START_TYPE_WARM_BOOT && m_veryFirstRun) + { + SWSS_LOG_WARN("warm start requested, but this is very first syncd start, forcing cold start"); + + /* + * We force cold start since if it's first run then redis db is not + * complete so redis asic view will not reflect warm boot asic state, + * if this happen then orch agent needs to be restarted as well to + * repopulate asic view. + */ + + m_commandLineOptions->m_startType = SAI_START_TYPE_COLD_BOOT; + } + + if (m_commandLineOptions->m_startType == SAI_START_TYPE_FASTFAST_BOOT) + { + /* + * Mellanox SAI requires to pass SAI_WARM_BOOT as SAI_BOOT_KEY + * to start 'fastfast' + */ + + m_profileMap[SAI_KEY_BOOT_TYPE] = std::to_string(SAI_START_TYPE_WARM_BOOT); + } + else + { + m_profileMap[SAI_KEY_BOOT_TYPE] = std::to_string(m_commandLineOptions->m_startType); // number value is needed + } +} + +bool Syncd::getAsicInitViewMode() const +{ + SWSS_LOG_ENTER(); + + return m_asicInitViewMode; +} + +void Syncd::setAsicInitViewMode( + _In_ bool enable) +{ + SWSS_LOG_ENTER(); + + m_asicInitViewMode = enable; +} + +bool Syncd::isInitViewMode() const +{ + SWSS_LOG_ENTER(); + + return m_asicInitViewMode && m_commandLineOptions->m_enableTempView; +} + +void Syncd::processEvent( + _In_ sairedis::SelectableChannel& consumer) +{ + SWSS_LOG_ENTER(); + + std::lock_guard lock(m_mutex); + + do + { + swss::KeyOpFieldsValuesTuple kco; + + /* + * In init mode we put all data to TEMP view and we snoop. We need + * to specify temporary view prefix in consumer since consumer puts + * data to redis db. + */ + + consumer.pop(kco, isInitViewMode()); + + processSingleEvent(kco); + } + while (!consumer.empty()); +} + +void Syncd::processEventInShutdownWaitMode( + _In_ sairedis::SelectableChannel& consumer) +{ + SWSS_LOG_ENTER(); + + // Syncd in shutdown-wait mode must respond to INIT_VIEW with FAILURE to avoid deadlock with OA + // This could happen because Orchagent sends INIT_VIEW before registering shutdown callback + // Can't reorder due to circular dependency: need switch to register callbacks, but + // need INIT_VIEW before creating switch + do + { + swss::KeyOpFieldsValuesTuple kco; + consumer.pop(kco, false); + + auto& op = kfvOp(kco); + auto& key = kfvKey(kco); + + SWSS_LOG_WARN("Received command while in shutdown-wait mode: op=%s, key=%s.", op.c_str(), key.c_str()); + + if (op == REDIS_ASIC_STATE_COMMAND_NOTIFY) + { + SWSS_LOG_ERROR("Syncd is waiting for shutdown, cannot process %s: Sending FAILURE response.", key.c_str()); + sendNotifyResponse(SAI_STATUS_FAILURE); + } + else + { + SWSS_LOG_WARN("Ignoring non-notify command in shutdown-wait mode"); + } + } + while (!consumer.empty()); +} + +sai_status_t Syncd::processSingleEvent( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + auto& key = kfvKey(kco); + auto& op = kfvOp(kco); + + SWSS_LOG_INFO("key: %s op: %s", key.c_str(), op.c_str()); + + if (key.length() == 0) + { + SWSS_LOG_DEBUG("no elements in m_buffer"); + + return SAI_STATUS_SUCCESS; + } + + WatchdogScope ws(m_timerWatchdog, op + ":" + key, &kco); + + if (op == REDIS_ASIC_STATE_COMMAND_CREATE) + return processQuadEvent(SAI_COMMON_API_CREATE, kco); + + if (op == REDIS_ASIC_STATE_COMMAND_REMOVE) + return processQuadEvent(SAI_COMMON_API_REMOVE, kco); + + if (op == REDIS_ASIC_STATE_COMMAND_SET) + return processQuadEvent(SAI_COMMON_API_SET, kco); + + if (op == REDIS_ASIC_STATE_COMMAND_GET) + return processQuadEvent(SAI_COMMON_API_GET, kco); + + if (op == REDIS_ASIC_STATE_COMMAND_BULK_CREATE) + return processBulkQuadEvent(SAI_COMMON_API_BULK_CREATE, kco); + + if (op == REDIS_ASIC_STATE_COMMAND_BULK_REMOVE) + return processBulkQuadEvent(SAI_COMMON_API_BULK_REMOVE, kco); + + if (op == REDIS_ASIC_STATE_COMMAND_BULK_SET) + return processBulkQuadEvent(SAI_COMMON_API_BULK_SET, kco); + + if (op == REDIS_ASIC_STATE_COMMAND_BULK_GET) + return processBulkQuadEvent(SAI_COMMON_API_BULK_GET, kco); + + if (op == REDIS_ASIC_STATE_COMMAND_NOTIFY) + return processNotifySyncd(kco); + + if (op == REDIS_ASIC_STATE_COMMAND_GET_STATS) + return processGetStatsEvent(kco); + + if (op == REDIS_ASIC_STATE_COMMAND_CLEAR_STATS) + return processClearStatsEvent(kco); + + if (op == REDIS_ASIC_STATE_COMMAND_FLUSH) + return processFdbFlush(kco); + + if (op == REDIS_ASIC_STATE_COMMAND_ATTR_CAPABILITY_QUERY) + return processAttrCapabilityQuery(kco); + + if (op == REDIS_ASIC_STATE_COMMAND_ATTR_ENUM_VALUES_CAPABILITY_QUERY) + return processAttrEnumValuesCapabilityQuery(kco); + + if (op == REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_QUERY) + return processObjectTypeGetAvailabilityQuery(kco); + + if (op == REDIS_FLEX_COUNTER_COMMAND_START_POLL) + return processFlexCounterEvent(key, SET_COMMAND, kfvFieldsValues(kco)); + + if (op == REDIS_FLEX_COUNTER_COMMAND_STOP_POLL) + return processFlexCounterEvent(key, DEL_COMMAND, kfvFieldsValues(kco)); + + if (op == REDIS_FLEX_COUNTER_COMMAND_SET_GROUP) + return processFlexCounterGroupEvent(key, SET_COMMAND, kfvFieldsValues(kco)); + + if (op == REDIS_FLEX_COUNTER_COMMAND_DEL_GROUP) + return processFlexCounterGroupEvent(key, DEL_COMMAND, kfvFieldsValues(kco)); + + if (op == REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_QUERY) + return processStatsCapabilityQuery(kco); + + if (op == REDIS_ASIC_STATE_COMMAND_STATS_ST_CAPABILITY_QUERY) + return processStatsStCapabilityQuery(kco); + + SWSS_LOG_THROW("event op '%s' is not implemented, FIXME", op.c_str()); +} + +sai_status_t Syncd::processAttrCapabilityQuery( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + auto& strSwitchVid = kfvKey(kco); + + sai_object_id_t switchVid; + sai_deserialize_object_id(strSwitchVid, switchVid); + + sai_object_id_t switchRid = m_translator->translateVidToRid(switchVid); + + auto& values = kfvFieldsValues(kco); + + if (values.size() != 2) + { + SWSS_LOG_ERROR("Invalid input: expected 2 arguments, received %zu", values.size()); + + m_selectableChannel->set(sai_serialize_status(SAI_STATUS_INVALID_PARAMETER), {}, REDIS_ASIC_STATE_COMMAND_ATTR_CAPABILITY_RESPONSE); + + return SAI_STATUS_INVALID_PARAMETER; + } + + sai_object_type_t objectType; + sai_deserialize_object_type(fvValue(values[0]), objectType); + + sai_attr_id_t attrId; + sai_deserialize_attr_id(fvValue(values[1]), attrId); + + sai_attr_capability_t capability; + + sai_status_t status = m_vendorSai->queryAttributeCapability(switchRid, objectType, attrId, &capability); + + std::vector entry; + + if (status == SAI_STATUS_SUCCESS) + { + entry = + { + swss::FieldValueTuple("CREATE_IMPLEMENTED", (capability.create_implemented ? "true" : "false")), + swss::FieldValueTuple("SET_IMPLEMENTED", (capability.set_implemented ? "true" : "false")), + swss::FieldValueTuple("GET_IMPLEMENTED", (capability.get_implemented ? "true" : "false")) + }; + + SWSS_LOG_INFO("Sending response: create_implemented:%d, set_implemented:%d, get_implemented:%d", + capability.create_implemented, capability.set_implemented, capability.get_implemented); + } + + m_selectableChannel->set(sai_serialize_status(status), entry, REDIS_ASIC_STATE_COMMAND_ATTR_CAPABILITY_RESPONSE); + + return status; +} + +sai_status_t Syncd::processAttrEnumValuesCapabilityQuery( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + auto& strSwitchVid = kfvKey(kco); + + sai_object_id_t switchVid; + sai_deserialize_object_id(strSwitchVid, switchVid); + + sai_object_id_t switchRid = m_translator->translateVidToRid(switchVid); + + auto& values = kfvFieldsValues(kco); + + if (values.size() != 3) + { + SWSS_LOG_ERROR("Invalid input: expected 3 arguments, received %zu", values.size()); + + m_selectableChannel->set(sai_serialize_status(SAI_STATUS_INVALID_PARAMETER), {}, REDIS_ASIC_STATE_COMMAND_ATTR_ENUM_VALUES_CAPABILITY_RESPONSE); + + return SAI_STATUS_INVALID_PARAMETER; + } + + sai_object_type_t objectType; + sai_deserialize_object_type(fvValue(values[0]), objectType); + + sai_attr_id_t attrId; + sai_deserialize_attr_id(fvValue(values[1]), attrId); + + uint32_t list_size = std::stoi(fvValue(values[2])); + + std::vector enum_capabilities_list(list_size); + + sai_s32_list_t enumCapList; + + enumCapList.count = list_size; + enumCapList.list = enum_capabilities_list.data(); + + sai_status_t status = m_vendorSai->queryAttributeEnumValuesCapability(switchRid, objectType, attrId, &enumCapList); + + std::vector entry; + + if (status == SAI_STATUS_SUCCESS) + { + std::vector vec; + std::transform(enumCapList.list, enumCapList.list + enumCapList.count, + std::back_inserter(vec), [](auto&e) { return std::to_string(e); }); + + std::ostringstream join; + std::copy(vec.begin(), vec.end(), std::ostream_iterator(join, ",")); + + auto strCap = join.str(); + + entry = + { + swss::FieldValueTuple("ENUM_CAPABILITIES", strCap), + swss::FieldValueTuple("ENUM_COUNT", std::to_string(enumCapList.count)) + }; + + SWSS_LOG_DEBUG("Sending response: capabilities = '%s', count = %d", strCap.c_str(), enumCapList.count); + } + else if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + entry = + { + swss::FieldValueTuple("ENUM_COUNT", std::to_string(enumCapList.count)) + }; + + SWSS_LOG_DEBUG("Sending response: count = %u", enumCapList.count); + } + + m_selectableChannel->set(sai_serialize_status(status), entry, REDIS_ASIC_STATE_COMMAND_ATTR_ENUM_VALUES_CAPABILITY_RESPONSE); + + return status; +} + +sai_status_t Syncd::processObjectTypeGetAvailabilityQuery( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + auto& strSwitchVid = kfvKey(kco); + + sai_object_id_t switchVid; + sai_deserialize_object_id(strSwitchVid, switchVid); + + const sai_object_id_t switchRid = m_translator->translateVidToRid(switchVid); + + std::vector values = kfvFieldsValues(kco); + + // Syncd needs to pop the object type off the end of the list in order to + // retrieve the attribute list + + sai_object_type_t objectType; + sai_deserialize_object_type(fvValue(values.back()), objectType); + + values.pop_back(); + + SaiAttributeList list(objectType, values, false); + + sai_attribute_t *attr_list = list.get_attr_list(); + + uint32_t attr_count = list.get_attr_count(); + + m_translator->translateVidToRid(objectType, attr_count, attr_list); + + uint64_t count; + + sai_status_t status = m_vendorSai->objectTypeGetAvailability( + switchRid, + objectType, + attr_count, + attr_list, + &count); + + std::vector entry; + + if (status == SAI_STATUS_SUCCESS) + { + entry.push_back(swss::FieldValueTuple("OBJECT_COUNT", std::to_string(count))); + + SWSS_LOG_DEBUG("Sending response: count = %lu", count); + } + + m_selectableChannel->set(sai_serialize_status(status), entry, REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_RESPONSE); + + return status; +} + +sai_status_t Syncd::processStatsCapabilityQuery( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + auto& strSwitchVid = kfvKey(kco); + + sai_object_id_t switchVid; + sai_deserialize_object_id(strSwitchVid, switchVid); + + sai_object_id_t switchRid = m_translator->translateVidToRid(switchVid); + + auto& values = kfvFieldsValues(kco); + + if (values.size() != 2) + { + SWSS_LOG_ERROR("Invalid input: expected 2 arguments, received %zu", values.size()); + + m_selectableChannel->set(sai_serialize_status(SAI_STATUS_INVALID_PARAMETER), {}, REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE); + + return SAI_STATUS_INVALID_PARAMETER; + } + + sai_object_type_t objectType; + sai_deserialize_object_type(fvValue(values[0]), objectType); + + uint32_t list_size = std::stoi(fvValue(values[1])); + + std::vector stat_capability_list(list_size); + + sai_stat_capability_list_t statCapList; + + statCapList.count = list_size; + statCapList.list = stat_capability_list.data(); + + sai_status_t status = m_vendorSai->queryStatsCapability(switchRid, objectType, &statCapList); + + std::vector entry; + + if (status == SAI_STATUS_SUCCESS) + { + std::vector vec_stat_enum; + std::vector vec_stat_modes; + + for (uint32_t it = 0; it < statCapList.count; it++) + { + vec_stat_enum.push_back(std::to_string(statCapList.list[it].stat_enum)); + vec_stat_modes.push_back(std::to_string(statCapList.list[it].stat_modes)); + } + + std::ostringstream join_stat_enum; + std::copy(vec_stat_enum.begin(), vec_stat_enum.end(), std::ostream_iterator(join_stat_enum, ",")); + auto strCapEnum = join_stat_enum.str(); + + std::ostringstream join_stat_modes; + std::copy(vec_stat_modes.begin(), vec_stat_modes.end(), std::ostream_iterator(join_stat_modes, ",")); + auto strCapModes = join_stat_modes.str(); + + entry = + { + swss::FieldValueTuple("STAT_ENUM", strCapEnum), + swss::FieldValueTuple("STAT_MODES", strCapModes), + swss::FieldValueTuple("STAT_COUNT", std::to_string(statCapList.count)) + }; + + SWSS_LOG_DEBUG("Sending response: stat_enums = '%s', stat_modes = '%s', count = %d", + strCapEnum.c_str(), strCapModes.c_str(), statCapList.count); + } + else if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + entry = { swss::FieldValueTuple("STAT_COUNT", std::to_string(statCapList.count)) }; + + SWSS_LOG_DEBUG("Sending response: count = %u", statCapList.count); + } + + m_selectableChannel->set(sai_serialize_status(status), entry, REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE); + + return status; +} + +sai_status_t Syncd::processStatsStCapabilityQuery( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + auto &strSwitchVid = kfvKey(kco); + + sai_object_id_t switchVid; + sai_deserialize_object_id(strSwitchVid, switchVid); + + sai_object_id_t switchRid = m_translator->translateVidToRid(switchVid); + + auto &values = kfvFieldsValues(kco); + + if (values.size() != 2) + { + SWSS_LOG_ERROR("Invalid input: expected 2 arguments, received %zu", values.size()); + + m_selectableChannel->set(sai_serialize_status(SAI_STATUS_INVALID_PARAMETER), {}, REDIS_ASIC_STATE_COMMAND_STATS_ST_CAPABILITY_RESPONSE); + + return SAI_STATUS_INVALID_PARAMETER; + } + + sai_object_type_t objectType; + sai_deserialize_object_type(fvValue(values[0]), objectType); + + uint32_t list_size = std::stoi(fvValue(values[1])); + + std::vector stat_capability_list(list_size); + + sai_stat_st_capability_list_t statCapList; + + statCapList.count = list_size; + statCapList.list = stat_capability_list.data(); + + sai_status_t status = m_vendorSai->queryStatsStCapability(switchRid, objectType, &statCapList); + + std::vector entry; + + if (status == SAI_STATUS_SUCCESS) + { + std::vector vec_stat_enum; + std::vector vec_stat_modes; + std::vector vec_minimal_polling_intervals; + + for (uint32_t it = 0; it < statCapList.count; it++) + { + vec_stat_enum.push_back(std::to_string(statCapList.list[it].capability.stat_enum)); + vec_stat_modes.push_back(std::to_string(statCapList.list[it].capability.stat_modes)); + vec_minimal_polling_intervals.push_back(std::to_string(statCapList.list[it].minimal_polling_interval)); + } + + std::ostringstream join_stat_enum; + std::copy(vec_stat_enum.begin(), vec_stat_enum.end(), std::ostream_iterator(join_stat_enum, ",")); + auto strCapEnum = join_stat_enum.str(); + + std::ostringstream join_stat_modes; + std::copy(vec_stat_modes.begin(), vec_stat_modes.end(), std::ostream_iterator(join_stat_modes, ",")); + auto strCapModes = join_stat_modes.str(); + + std::ostringstream join_minimal_polling_intervals; + std::copy(vec_minimal_polling_intervals.begin(), vec_minimal_polling_intervals.end(), std::ostream_iterator(join_minimal_polling_intervals, ",")); + auto strCapMinPollInt = join_minimal_polling_intervals.str(); + + entry = + { + swss::FieldValueTuple("STAT_ENUM", strCapEnum), + swss::FieldValueTuple("STAT_MODES", strCapModes), + swss::FieldValueTuple("MINIMAL_POLLING_INTERVALS", strCapMinPollInt), + swss::FieldValueTuple("STAT_COUNT", std::to_string(statCapList.count))}; + + SWSS_LOG_DEBUG("Sending response: stat_enums = '%s', stat_modes = '%s', minimal_polling_intervals = '%s' count = %d", + strCapEnum.c_str(), strCapModes.c_str(), strCapMinPollInt.c_str(), statCapList.count); + } + else if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + entry = {swss::FieldValueTuple("STAT_COUNT", std::to_string(statCapList.count))}; + + SWSS_LOG_DEBUG("Sending response: count = %u", statCapList.count); + } + + m_selectableChannel->set(sai_serialize_status(status), entry, REDIS_ASIC_STATE_COMMAND_STATS_ST_CAPABILITY_RESPONSE); + + return status; +} + +sai_status_t Syncd::processFdbFlush( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + auto& key = kfvKey(kco); + auto strSwitchVid = key.substr(key.find(":") + 1); + + sai_object_id_t switchVid; + sai_deserialize_object_id(strSwitchVid, switchVid); + + sai_object_id_t switchRid = m_translator->translateVidToRid(switchVid); + + auto& values = kfvFieldsValues(kco); + + for (const auto &v: values) + { + SWSS_LOG_DEBUG("attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str()); + } + + SaiAttributeList list(SAI_OBJECT_TYPE_FDB_FLUSH, values, false); + SaiAttributeList vidlist(SAI_OBJECT_TYPE_FDB_FLUSH, values, false); + + /* + * Attribute list can't be const since we will use it to translate VID to + * RID in place. + */ + + sai_attribute_t *attr_list = list.get_attr_list(); + uint32_t attr_count = list.get_attr_count(); + + m_translator->translateVidToRid(SAI_OBJECT_TYPE_FDB_FLUSH, attr_count, attr_list); + + sai_status_t status = m_vendorSai->flushFdbEntries(switchRid, attr_count, attr_list); + + m_selectableChannel->set(sai_serialize_status(status), {} , REDIS_ASIC_STATE_COMMAND_FLUSHRESPONSE); + + if (status == SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("fdb flush succeeded, updating redis database"); + + // update database right after fdb flush success (not in notification) + // build artificial notification here to reuse code + + auto *md = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_FDB_FLUSH, SAI_FDB_FLUSH_ATTR_ENTRY_TYPE); + auto *dv = md ? md->defaultvalue : nullptr; + + sai_fdb_flush_entry_type_t type = dv + ? (sai_fdb_flush_entry_type_t)dv->s32 + : SAI_FDB_FLUSH_ENTRY_TYPE_DYNAMIC; + + sai_object_id_t bvId = SAI_NULL_OBJECT_ID; + sai_object_id_t bridgePortId = SAI_NULL_OBJECT_ID; + + attr_list = vidlist.get_attr_list(); + attr_count = vidlist.get_attr_count(); + + for (uint32_t i = 0; i < attr_count; i++) + { + switch (attr_list[i].id) + { + case SAI_FDB_FLUSH_ATTR_BRIDGE_PORT_ID: + bridgePortId = attr_list[i].value.oid; + break; + + case SAI_FDB_FLUSH_ATTR_BV_ID: + bvId = attr_list[i].value.oid; + break; + + case SAI_FDB_FLUSH_ATTR_ENTRY_TYPE: + type = (sai_fdb_flush_entry_type_t)attr_list[i].value.s32; + break; + + default: + SWSS_LOG_ERROR("unsupported attribute: %d, skipping", attr_list[i].id); + break; + } + } + + m_client->processFlushEvent(switchVid, bridgePortId, bvId, type); + } + + return status; +} + +sai_status_t Syncd::processClearStatsEvent( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + const std::string &key = kfvKey(kco); + + sai_object_meta_key_t metaKey; + sai_deserialize_object_meta_key(key, metaKey); + + if (isInitViewMode() && m_createdInInitView.find(metaKey.objectkey.key.object_id) != m_createdInInitView.end()) + { + SWSS_LOG_WARN("CLEAR STATS api can't be used on %s since it's created in INIT_VIEW mode", key.c_str()); + + sai_status_t status = SAI_STATUS_INVALID_OBJECT_ID; + + m_selectableChannel->set(sai_serialize_status(status), {}, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); + + return status; + } + + if (!m_translator->tryTranslateVidToRid(metaKey)) + { + SWSS_LOG_WARN("VID to RID translation failure: %s", key.c_str()); + sai_status_t status = SAI_STATUS_INVALID_OBJECT_ID; + m_selectableChannel->set(sai_serialize_status(status), {}, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); + return status; + } + + auto info = sai_metadata_get_object_type_info(metaKey.objecttype); + + if (info->isnonobjectid) + { + SWSS_LOG_THROW("non object id not supported on clear stats: %s, FIXME", key.c_str()); + } + + std::vector counter_ids; + + for (auto&v: kfvFieldsValues(kco)) + { + int32_t val; + sai_deserialize_enum(fvField(v), info->statenum, val); + + counter_ids.push_back(val); + } + + auto status = m_vendorSai->clearStats( + metaKey.objecttype, + metaKey.objectkey.key.object_id, + (uint32_t)counter_ids.size(), + counter_ids.data()); + + m_selectableChannel->set(sai_serialize_status(status), {}, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); + + return status; +} + +sai_status_t Syncd::processGetStatsEvent( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + const std::string &key = kfvKey(kco); + + sai_object_meta_key_t metaKey; + sai_deserialize_object_meta_key(key, metaKey); + + if (isInitViewMode() && m_createdInInitView.find(metaKey.objectkey.key.object_id) != m_createdInInitView.end()) + { + SWSS_LOG_WARN("GET STATS api can't be used on %s since it's created in INIT_VIEW mode", key.c_str()); + + sai_status_t status = SAI_STATUS_INVALID_OBJECT_ID; + + m_selectableChannel->set(sai_serialize_status(status), {}, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); + + return status; + } + + m_translator->translateVidToRid(metaKey); + + auto info = sai_metadata_get_object_type_info(metaKey.objecttype); + + if (info->isnonobjectid) + { + SWSS_LOG_THROW("non object id not supported on clear stats: %s, FIXME", key.c_str()); + } + + std::vector counter_ids; + + for (auto&v: kfvFieldsValues(kco)) + { + int32_t val; + sai_deserialize_enum(fvField(v), info->statenum, val); + + counter_ids.push_back(val); + } + + std::vector result(counter_ids.size()); + + auto status = m_vendorSai->getStats( + metaKey.objecttype, + metaKey.objectkey.key.object_id, + (uint32_t)counter_ids.size(), + counter_ids.data(), + result.data()); + + std::vector entry; + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("Getting stats error: %s", sai_serialize_status(status).c_str()); + } + else + { + const auto& values = kfvFieldsValues(kco); + + for (size_t i = 0; i < values.size(); i++) + { + entry.emplace_back(fvField(values[i]), std::to_string(result[i])); + } + } + + m_selectableChannel->set(sai_serialize_status(status), entry, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); + + return status; +} + +sai_status_t Syncd::processBulkQuadEvent( + _In_ sai_common_api_t api, + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + const std::string& key = kfvKey(kco); // objectType:count + + std::string strObjectType = key.substr(0, key.find(":")); + + sai_object_type_t objectType; + sai_deserialize_object_type(strObjectType, objectType); + + const std::vector &values = kfvFieldsValues(kco); + + std::vector> strAttributes; + + // field = objectId + // value = attrid=attrvalue|... + + std::vector objectIds; + + std::vector> attributes; + + for (const auto &fvt: values) + { + std::string strObjectId = fvField(fvt); + std::string joined = fvValue(fvt); + + // decode values + + auto v = swss::tokenize(joined, '|'); + + objectIds.push_back(strObjectId); + + std::vector entries; // attributes per object id + + for (size_t i = 0; i < v.size(); ++i) + { + const std::string item = v.at(i); + + auto start = item.find_first_of("="); + + auto field = item.substr(0, start); + auto value = item.substr(start + 1); + + entries.emplace_back(field, value); + } + + strAttributes.push_back(entries); + + // since now we converted this to proper list, we can extract attributes + + auto list = std::make_shared(objectType, entries, false); + + attributes.push_back(list); + } + + SWSS_LOG_INFO("bulk %s executing with %zu items", + strObjectType.c_str(), + objectIds.size()); + + if (isInitViewMode()) + { + return processBulkQuadEventInInitViewMode(objectType, objectIds, api, attributes, strAttributes); + } + + if (api != SAI_COMMON_API_BULK_GET) + { + // translate attributes for all objects + + for (auto &list: attributes) + { + sai_attribute_t *attr_list = list->get_attr_list(); + uint32_t attr_count = list->get_attr_count(); + + m_translator->translateVidToRid(objectType, attr_count, attr_list); + } + } + + auto info = sai_metadata_get_object_type_info(objectType); + + if (info->isobjectid) + { + return processBulkOid(objectType, objectIds, api, attributes, strAttributes); + } + else + { + return processBulkEntry(objectType, objectIds, api, attributes, strAttributes); + } +} + +sai_status_t Syncd::processBulkQuadEventInInitViewMode( + _In_ sai_object_type_t objectType, + _In_ const std::vector& objectIds, + _In_ sai_common_api_t api, + _In_ const std::vector>& attributes, + _In_ const std::vector>& strAttributes) +{ + SWSS_LOG_ENTER(); + + const auto objectCount = static_cast(objectIds.size()); + + std::vector statuses(objectIds.size()); + + const sai_status_t initialObjectStatus = api != SAI_COMMON_API_BULK_GET ? SAI_STATUS_SUCCESS : SAI_STATUS_NOT_EXECUTED; + statuses.assign(statuses.size(), initialObjectStatus); + + auto info = sai_metadata_get_object_type_info(objectType); + + switch (api) + { + case SAI_COMMON_API_BULK_CREATE: + case SAI_COMMON_API_BULK_REMOVE: + + if (info->isnonobjectid) + { + sendApiResponse(api, SAI_STATUS_SUCCESS, (uint32_t)statuses.size(), statuses.data()); + + syncUpdateRedisBulkQuadEvent(api, statuses, objectType, objectIds, strAttributes); + + return SAI_STATUS_SUCCESS; + } + + switch (objectType) + { + case SAI_OBJECT_TYPE_SWITCH: + case SAI_OBJECT_TYPE_PORT: + case SAI_OBJECT_TYPE_SCHEDULER_GROUP: + case SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP: + + SWSS_LOG_THROW("%s is not supported in init view mode", + sai_serialize_object_type(objectType).c_str()); + + default: + + sendApiResponse(api, SAI_STATUS_SUCCESS, (uint32_t)statuses.size(), statuses.data()); + + syncUpdateRedisBulkQuadEvent(api, statuses, objectType, objectIds, strAttributes); + + for (auto& str: objectIds) + { + sai_object_id_t objectVid; + sai_deserialize_object_id(str, objectVid); + + // in init view mode insert every created object except switch + + m_createdInInitView.insert(objectVid); + } + + return SAI_STATUS_SUCCESS; + } + + case SAI_COMMON_API_BULK_SET: + + switch (objectType) + { + case SAI_OBJECT_TYPE_SWITCH: + case SAI_OBJECT_TYPE_SCHEDULER_GROUP: + + SWSS_LOG_THROW("%s is not supported in init view mode", + sai_serialize_object_type(objectType).c_str()); + + default: + + break; + } + + sendApiResponse(api, SAI_STATUS_SUCCESS, (uint32_t)statuses.size(), statuses.data()); + + syncUpdateRedisBulkQuadEvent(api, statuses, objectType, objectIds, strAttributes); + + return SAI_STATUS_SUCCESS; + + case SAI_COMMON_API_BULK_GET: + if (info->isnonobjectid) + { + /* + * Those objects are user created, so if user created ROUTE he + * passed some attributes, there is no sense to support GET + * since user explicitly know what attributes were set, similar + * for other non object id types. + */ + + SWSS_LOG_ERROR("get is not supported on %s in init view mode", sai_serialize_object_type(objectType).c_str()); + + const sai_status_t status = SAI_STATUS_NOT_SUPPORTED; + sendBulkGetResponse(objectType, objectIds, status, attributes, statuses); + + return status; + } + else + { + for (size_t idx = 0; idx < objectCount; idx++) + { + const auto& strObjectId = objectIds[idx]; + + sai_object_id_t objectVid; + sai_deserialize_object_id(strObjectId, objectVid); + + if (isInitViewMode() && m_createdInInitView.find(objectVid) != m_createdInInitView.end()) + { + SWSS_LOG_WARN("GET api can't be used on %s (%s) since it's created in INIT_VIEW mode", + strObjectId.c_str(), + sai_serialize_object_type(objectType).c_str()); + + const sai_status_t status = SAI_STATUS_INVALID_OBJECT_ID; + sendBulkGetResponse(objectType, objectIds, status, attributes, statuses); + + return status; + } + + } + + return processBulkOid(objectType, objectIds, SAI_COMMON_API_BULK_GET, attributes, strAttributes); + } + + default: + + SWSS_LOG_THROW("common bulk api (%s) is not implemented in init view mode", + sai_serialize_common_api(api).c_str()); + } +} + +sai_status_t Syncd::processBulkCreateEntry( + _In_ sai_object_type_t objectType, + _In_ const std::vector& objectIds, + _In_ const std::vector>& attributes, + _Out_ std::vector& statuses) +{ + SWSS_LOG_ENTER(); + sai_status_t status = SAI_STATUS_SUCCESS; + + uint32_t object_count = (uint32_t) objectIds.size(); + + if (!object_count) + { + SWSS_LOG_ERROR("container with objectIds is empty in processBulkCreateEntry"); + return SAI_STATUS_FAILURE; + } + + sai_bulk_op_error_mode_t mode = SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR; + + std::vector attr_counts(object_count); + std::vector attr_lists(object_count); + + for (uint32_t idx = 0; idx < object_count; idx++) + { + attr_counts[idx] = attributes[idx]->get_attr_count(); + attr_lists[idx] = attributes[idx]->get_attr_list(); + } + + switch ((int)objectType) + { + case SAI_OBJECT_TYPE_ROUTE_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_route_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); + } + + static PerformanceIntervalTimer timer("Syncd::processBulkCreateEntry(route_entry) CREATE"); + + timer.start(); + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + + timer.stop(); + + timer.inc(object_count); + } + break; + + case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_neighbor_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].rif_id = m_translator->translateVidToRid(entries[it].rif_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_FDB_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_fdb_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].bv_id = m_translator->translateVidToRid(entries[it].bv_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_NAT_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_nat_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_INSEG_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_inseg_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_MY_SID_ENTRY: + { + std::vector entries(object_count); + + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_my_sid_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY: + { + std::vector entries(object_count); + + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_direction_lookup_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY: + { + std::vector entries(object_count); + + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_eni_ether_address_map_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_VIP_ENTRY: + { + std::vector entries(object_count); + + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_vip_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_INBOUND_ROUTING_ENTRY: + { + std::vector entries(object_count); + + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_inbound_routing_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].eni_id = m_translator->translateVidToRid(entries[it].eni_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_PA_VALIDATION_ENTRY: + { + std::vector entries(object_count); + + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_pa_validation_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].vnet_id = m_translator->translateVidToRid(entries[it].vnet_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY: + { + std::vector entries(object_count); + + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_outbound_routing_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].outbound_routing_group_id = m_translator->translateVidToRid(entries[it].outbound_routing_group_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY: + { + std::vector entries(object_count); + + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_outbound_ca_to_pa_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].dst_vnet_id = m_translator->translateVidToRid(entries[it].dst_vnet_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY: + { + std::vector entries(object_count); + + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_outbound_port_map_port_range_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].outbound_port_map_id = m_translator->translateVidToRid(entries[it].outbound_port_map_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_GLOBAL_TRUSTED_VNI_ENTRY: + { + std::vector entries(object_count); + + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_global_trusted_vni_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_ENI_TRUSTED_VNI_ENTRY: + { + std::vector entries(object_count); + + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_eni_trusted_vni_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].eni_id = m_translator->translateVidToRid(entries[it].eni_id); + } + + status = m_vendorSai->bulkCreate( + object_count, + entries.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + } + break; + + default: + return SAI_STATUS_NOT_SUPPORTED; + } + + return status; +} + +sai_status_t Syncd::processBulkRemoveEntry( + _In_ sai_object_type_t objectType, + _In_ const std::vector& objectIds, + _Out_ std::vector& statuses) +{ + SWSS_LOG_ENTER(); + + sai_status_t status = SAI_STATUS_SUCCESS; + + uint32_t object_count = (uint32_t) objectIds.size(); + + if (!object_count) + { + SWSS_LOG_ERROR("container with objectIds is empty in processBulkRemoveEntry"); + return SAI_STATUS_FAILURE; + } + + sai_bulk_op_error_mode_t mode = SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR; + + switch ((int)objectType) + { + case SAI_OBJECT_TYPE_ROUTE_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_route_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_neighbor_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].rif_id = m_translator->translateVidToRid(entries[it].rif_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_FDB_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_fdb_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].bv_id = m_translator->translateVidToRid(entries[it].bv_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_NAT_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_nat_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_MY_SID_ENTRY: + { + std::vector entries(object_count); + + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_my_sid_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_INSEG_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_inseg_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_direction_lookup_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_eni_ether_address_map_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_VIP_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_vip_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_INBOUND_ROUTING_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_inbound_routing_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].eni_id = m_translator->translateVidToRid(entries[it].eni_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_PA_VALIDATION_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_pa_validation_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].vnet_id = m_translator->translateVidToRid(entries[it].vnet_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_outbound_routing_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].outbound_routing_group_id = m_translator->translateVidToRid(entries[it].outbound_routing_group_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_outbound_ca_to_pa_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].dst_vnet_id = m_translator->translateVidToRid(entries[it].dst_vnet_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_outbound_port_map_port_range_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].outbound_port_map_id = m_translator->translateVidToRid(entries[it].outbound_port_map_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_GLOBAL_TRUSTED_VNI_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_global_trusted_vni_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_ENI_TRUSTED_VNI_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_eni_trusted_vni_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].eni_id = m_translator->translateVidToRid(entries[it].eni_id); + } + + status = m_vendorSai->bulkRemove( + object_count, + entries.data(), + mode, + statuses.data()); + + } + break; + + default: + return SAI_STATUS_NOT_SUPPORTED; + } + + return status; +} + +sai_status_t Syncd::processBulkSetEntry( + _In_ sai_object_type_t objectType, + _In_ const std::vector& objectIds, + _In_ const std::vector>& attributes, + _Out_ std::vector& statuses) +{ + SWSS_LOG_ENTER(); + + sai_status_t status = SAI_STATUS_SUCCESS; + + std::vector attr_lists; + + uint32_t object_count = (uint32_t) objectIds.size(); + + if (!object_count) + { + SWSS_LOG_ERROR("container with objectIds is empty in processBulkSetEntry"); + return SAI_STATUS_FAILURE; + } + + sai_bulk_op_error_mode_t mode = SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR; + + for (uint32_t it = 0; it < object_count; it++) + { + attr_lists.push_back(attributes[it]->get_attr_list()[0]); + } + + switch ((int)objectType) + { + case SAI_OBJECT_TYPE_ROUTE_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_route_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); + } + + status = m_vendorSai->bulkSet( + object_count, + entries.data(), + attr_lists.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_neighbor_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].rif_id = m_translator->translateVidToRid(entries[it].rif_id); + } + + status = m_vendorSai->bulkSet( + object_count, + entries.data(), + attr_lists.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_FDB_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_fdb_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].bv_id = m_translator->translateVidToRid(entries[it].bv_id); + } + + status = m_vendorSai->bulkSet( + object_count, + entries.data(), + attr_lists.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_NAT_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_nat_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); + } + + status = m_vendorSai->bulkSet( + object_count, + entries.data(), + attr_lists.data(), + mode, + statuses.data()); + + } + break; + + case SAI_OBJECT_TYPE_MY_SID_ENTRY: + { + std::vector entries(object_count); + + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_my_sid_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); + } + + status = m_vendorSai->bulkSet( + object_count, + entries.data(), + attr_lists.data(), + mode, + statuses.data()); + } + break; + + case SAI_OBJECT_TYPE_INSEG_ENTRY: + { + std::vector entries(object_count); + for (uint32_t it = 0; it < object_count; it++) + { + sai_deserialize_inseg_entry(objectIds[it], entries[it]); + + entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); + } + + status = m_vendorSai->bulkSet( + object_count, + entries.data(), + attr_lists.data(), + mode, + statuses.data()); + + } + break; + + default: + return SAI_STATUS_NOT_SUPPORTED; + } + + return status; +} + +sai_status_t Syncd::processBulkEntry( + _In_ sai_object_type_t objectType, + _In_ const std::vector& objectIds, + _In_ sai_common_api_t api, + _In_ const std::vector>& attributes, + _In_ const std::vector>& strAttributes) +{ + SWSS_LOG_ENTER(); + + auto info = sai_metadata_get_object_type_info(objectType); + + if (info->isobjectid) + { + SWSS_LOG_THROW("passing oid object to bulk non object id operation"); + } + + std::vector statuses(objectIds.size()); + + sai_status_t all = SAI_STATUS_SUCCESS; + + if (m_commandLineOptions->m_enableSaiBulkSupport) + { + switch (api) + { + case SAI_COMMON_API_BULK_CREATE: + all = processBulkCreateEntry(objectType, objectIds, attributes, statuses); + break; + + case SAI_COMMON_API_BULK_REMOVE: + all = processBulkRemoveEntry(objectType, objectIds, statuses); + break; + + case SAI_COMMON_API_BULK_SET: + all = processBulkSetEntry(objectType, objectIds, attributes, statuses); + break; + + default: + SWSS_LOG_ERROR("api %s is not supported in bulk", sai_serialize_common_api(api).c_str()); + all = SAI_STATUS_NOT_SUPPORTED; + } + + if (all != SAI_STATUS_NOT_SUPPORTED && all != SAI_STATUS_NOT_IMPLEMENTED) + { + sendApiResponse(api, all, (uint32_t)objectIds.size(), statuses.data()); + syncUpdateRedisBulkQuadEvent(api, statuses, objectType, objectIds, strAttributes); + + return all; + } + } + + // vendor SAI don't bulk API yet, so execute one by one + + all = SAI_STATUS_SUCCESS; + + for (size_t idx = 0; idx < objectIds.size(); ++idx) + { + sai_object_meta_key_t metaKey; + + metaKey.objecttype = objectType; + + switch ((int)objectType) + { + case SAI_OBJECT_TYPE_ROUTE_ENTRY: + sai_deserialize_route_entry(objectIds[idx], metaKey.objectkey.key.route_entry); + break; + + case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY: + sai_deserialize_neighbor_entry(objectIds[idx], metaKey.objectkey.key.neighbor_entry); + break; + + case SAI_OBJECT_TYPE_NAT_ENTRY: + sai_deserialize_nat_entry(objectIds[idx], metaKey.objectkey.key.nat_entry); + break; + + case SAI_OBJECT_TYPE_FDB_ENTRY: + sai_deserialize_fdb_entry(objectIds[idx], metaKey.objectkey.key.fdb_entry); + break; + + case SAI_OBJECT_TYPE_INSEG_ENTRY: + sai_deserialize_inseg_entry(objectIds[idx], metaKey.objectkey.key.inseg_entry); + break; + + case SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY: + sai_deserialize_direction_lookup_entry(objectIds[idx], metaKey.objectkey.key.direction_lookup_entry); + break; + + case SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY: + sai_deserialize_eni_ether_address_map_entry(objectIds[idx], metaKey.objectkey.key.eni_ether_address_map_entry); + break; + + case SAI_OBJECT_TYPE_VIP_ENTRY: + sai_deserialize_vip_entry(objectIds[idx], metaKey.objectkey.key.vip_entry); + break; + + case SAI_OBJECT_TYPE_INBOUND_ROUTING_ENTRY: + sai_deserialize_inbound_routing_entry(objectIds[idx], metaKey.objectkey.key.inbound_routing_entry); + break; + + case SAI_OBJECT_TYPE_PA_VALIDATION_ENTRY: + sai_deserialize_pa_validation_entry(objectIds[idx], metaKey.objectkey.key.pa_validation_entry); + break; + + case SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY: + sai_deserialize_outbound_routing_entry(objectIds[idx], metaKey.objectkey.key.outbound_routing_entry); + break; + + case SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY: + sai_deserialize_outbound_ca_to_pa_entry(objectIds[idx], metaKey.objectkey.key.outbound_ca_to_pa_entry); + break; + + case SAI_OBJECT_TYPE_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY: + sai_deserialize_outbound_port_map_port_range_entry(objectIds[idx], metaKey.objectkey.key.outbound_port_map_port_range_entry); + break; + + case SAI_OBJECT_TYPE_GLOBAL_TRUSTED_VNI_ENTRY: + sai_deserialize_global_trusted_vni_entry(objectIds[idx], metaKey.objectkey.key.global_trusted_vni_entry); + break; + + case SAI_OBJECT_TYPE_ENI_TRUSTED_VNI_ENTRY: + sai_deserialize_eni_trusted_vni_entry(objectIds[idx], metaKey.objectkey.key.eni_trusted_vni_entry); + break; + + default: + SWSS_LOG_THROW("object %s not implemented, FIXME", sai_serialize_object_type(objectType).c_str()); + } + + sai_status_t status = SAI_STATUS_FAILURE; + + auto& list = attributes[idx]; + + sai_attribute_t *attr_list = list->get_attr_list(); + uint32_t attr_count = list->get_attr_count(); + + if (api == SAI_COMMON_API_BULK_CREATE) + { + if (objectType == SAI_OBJECT_TYPE_ROUTE_ENTRY) + { + static PerformanceIntervalTimer timer("Syncd::processBulkEntry::processEntry(route_entry) CREATE"); + + timer.start(); + + status = processEntry(metaKey, SAI_COMMON_API_CREATE, attr_count, attr_list); + + timer.stop(); + + timer.inc(); + } + else + { + status = processEntry(metaKey, SAI_COMMON_API_CREATE, attr_count, attr_list); + } + } + else if (api == SAI_COMMON_API_BULK_REMOVE) + { + status = processEntry(metaKey, SAI_COMMON_API_REMOVE, attr_count, attr_list); + } + else if (api == SAI_COMMON_API_BULK_SET) + { + status = processEntry(metaKey, SAI_COMMON_API_SET, attr_count, attr_list); + } + else + { + SWSS_LOG_THROW("api %d is not supported in bulk mode", api); + } + + if (api != SAI_COMMON_API_BULK_GET && status != SAI_STATUS_SUCCESS) + { + if (!m_enableSyncMode) + { + SWSS_LOG_THROW("operation %s for %s failed in async mode!", + sai_serialize_common_api(api).c_str(), + sai_serialize_object_type(objectType).c_str()); + } + + all = SAI_STATUS_FAILURE; // all can be success if all has been success + } + + statuses[idx] = status; + } + + sendApiResponse(api, all, (uint32_t)objectIds.size(), statuses.data()); + + syncUpdateRedisBulkQuadEvent(api, statuses, objectType, objectIds, strAttributes); + + return all; +} + +sai_status_t Syncd::processEntry( + _In_ sai_object_meta_key_t metaKey, + _In_ sai_common_api_t api, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + m_translator->translateVidToRid(metaKey); + + switch (api) + { + case SAI_COMMON_API_CREATE: + return m_vendorSai->create(metaKey, SAI_NULL_OBJECT_ID, attr_count, attr_list); + + case SAI_COMMON_API_REMOVE: + return m_vendorSai->remove(metaKey); + + case SAI_COMMON_API_SET: + return m_vendorSai->set(metaKey, attr_list); + + case SAI_COMMON_API_GET: + return m_vendorSai->get(metaKey, attr_count, attr_list); + + default: + + SWSS_LOG_THROW("api %s not supported", sai_serialize_common_api(api).c_str()); + } +} + +sai_status_t Syncd::processBulkOidCreate( + _In_ sai_object_type_t objectType, + _In_ sai_bulk_op_error_mode_t mode, + _In_ const std::vector& objectIds, + _In_ const std::vector>& attributes, + _Out_ std::vector& statuses) +{ + SWSS_LOG_ENTER(); + + sai_status_t status = SAI_STATUS_SUCCESS; + uint32_t object_count = (uint32_t)objectIds.size(); + + if (!object_count) + { + SWSS_LOG_ERROR("container with objectIds is empty in processBulkOidCreate"); + return SAI_STATUS_FAILURE; + } + + std::vector objectVids(object_count); + + std::vector attr_counts(object_count); + std::vector attr_lists(object_count); + + for (size_t idx = 0; idx < object_count; idx++) + { + sai_deserialize_object_id(objectIds[idx], objectVids[idx]); + + attr_counts[idx] = attributes[idx]->get_attr_count(); + attr_lists[idx] = attributes[idx]->get_attr_list(); + } + + sai_object_id_t switchRid = SAI_NULL_OBJECT_ID; + + sai_object_id_t switchVid = VidManager::switchIdQuery(objectVids.front()); + switchRid = m_translator->translateVidToRid(switchVid); + + + std::vector objectRids(object_count); + + status = m_vendorSai->bulkCreate( + objectType, + switchRid, + object_count, + attr_counts.data(), + attr_lists.data(), + mode, + objectRids.data(), + statuses.data()); + + if (status == SAI_STATUS_NOT_IMPLEMENTED || status == SAI_STATUS_NOT_SUPPORTED) + { + SWSS_LOG_WARN("bulkCreate api is not implemented or not supported, object_type = %s", + sai_serialize_object_type(objectType).c_str()); + return status; + } + + /* + * Create vectors for successfully created objects only, since objectRids/Vids + * contain both successful and failed entries. Only store successful mappings + * in Redis. + */ + std::vector createdRids, createdVids; + createdRids.reserve(object_count); + createdVids.reserve(object_count); + + for (size_t idx = 0; idx < object_count; idx++) + { + if (statuses[idx] == SAI_STATUS_SUCCESS) + { + createdRids.push_back(objectRids[idx]); + createdVids.push_back(objectVids[idx]); + } + } + + m_translator->insertRidsAndVids(createdRids.size(), createdRids.data(), createdVids.data()); + + if (objectType == SAI_OBJECT_TYPE_PORT) + { + m_switches.at(switchVid)->onPostPortsCreate(createdRids.size(), createdRids.data()); + } + + return status; +} + +sai_status_t Syncd::processBulkOidSet( + _In_ sai_object_type_t objectType, + _In_ sai_bulk_op_error_mode_t mode, + _In_ const std::vector& objectIds, + _In_ const std::vector>& attributes, + _Out_ std::vector& statuses) +{ + SWSS_LOG_ENTER(); + + sai_status_t status = SAI_STATUS_SUCCESS; + uint32_t object_count = static_cast(objectIds.size()); + + if (!object_count) + { + SWSS_LOG_ERROR("container with objectIds is empty in processBulkOidSet"); + return SAI_STATUS_FAILURE; + } + + std::vector objectVids(object_count); + std::vector objectRids(object_count); + + std::vector attr_list(object_count); + + for (size_t idx = 0; idx < object_count; idx++) + { + sai_deserialize_object_id(objectIds[idx], objectVids[idx]); + objectRids[idx] = m_translator->translateVidToRid(objectVids[idx]); + + const auto attr_count = attributes[idx]->get_attr_count(); + if (attr_count != 1) + { + SWSS_LOG_THROW("bulkSet api requires one attribute per object"); + } + + attr_list[idx] = *attributes[idx]->get_attr_list(); + } + + status = m_vendorSai->bulkSet( + objectType, + object_count, + objectRids.data(), + attr_list.data(), + mode, + statuses.data()); + + if (status == SAI_STATUS_NOT_IMPLEMENTED || status == SAI_STATUS_NOT_SUPPORTED) + { + SWSS_LOG_WARN("bulkSet api is not implemented or not supported, object_type = %s", + sai_serialize_object_type(objectType).c_str()); + } + + return status; +} + +sai_status_t Syncd::processBulkOidGet( + _In_ sai_object_type_t objectType, + _In_ sai_bulk_op_error_mode_t mode, + _In_ const std::vector& objectIds, + _In_ const std::vector>& attributes, + _Out_ std::vector& statuses) +{ + SWSS_LOG_ENTER(); + + const auto object_count = static_cast(objectIds.size()); + + if (!object_count) + { + SWSS_LOG_ERROR("container with objectIds is empty in processBulkOidGet"); + return SAI_STATUS_FAILURE; + } + + std::vector objectVids(object_count); + std::vector objectRids(object_count); + + std::vector attr_counts(object_count); + std::vector attr_lists(object_count); + + for (size_t idx = 0; idx < object_count; idx++) + { + sai_deserialize_object_id(objectIds[idx], objectVids[idx]); + objectRids[idx] = m_translator->translateVidToRid(objectVids[idx]); + + attr_counts[idx] = attributes[idx]->get_attr_count(); + attr_lists[idx] = attributes[idx]->get_attr_list(); + } + + const auto status = m_vendorSai->bulkGet(objectType, + object_count, + objectRids.data(), + attr_counts.data(), + attr_lists.data(), + mode, + statuses.data()); + + if (status == SAI_STATUS_NOT_IMPLEMENTED || status == SAI_STATUS_NOT_SUPPORTED) + { + SWSS_LOG_WARN("bulkGet api is not implemented or not supported, object_type = %s", + sai_serialize_object_type(objectType).c_str()); + return status; + } + + return status; +} + +sai_status_t Syncd::processBulkOidRemove( + _In_ sai_object_type_t objectType, + _In_ sai_bulk_op_error_mode_t mode, + _In_ const std::vector& objectIds, + _Out_ std::vector& statuses) +{ + SWSS_LOG_ENTER(); + + sai_status_t status = SAI_STATUS_SUCCESS; + uint32_t object_count = (uint32_t)objectIds.size(); + + if (!object_count) + { + SWSS_LOG_ERROR("container with objectIds is empty in processBulkOidRemove"); + return SAI_STATUS_FAILURE; + } + + std::vector objectVids(object_count); + std::vector objectRids(object_count); + + for (size_t idx = 0; idx < object_count; idx++) + { + sai_deserialize_object_id(objectIds[idx], objectVids[idx]); + objectRids[idx] = m_translator->translateVidToRid(objectVids[idx]); + + if (objectType == SAI_OBJECT_TYPE_PORT) + { + sai_object_id_t switchVid = VidManager::switchIdQuery(objectVids[idx]); + m_switches.at(switchVid)->collectPortRelatedObjects(objectRids[idx]); + } + } + + status = m_vendorSai->bulkRemove( + objectType, + (uint32_t)object_count, + objectRids.data(), + mode, + statuses.data()); + + if (status == SAI_STATUS_NOT_IMPLEMENTED || status == SAI_STATUS_NOT_SUPPORTED) + { + SWSS_LOG_WARN("bulkRemove api is not implemented or not supported, object_type = %s", + sai_serialize_object_type(objectType).c_str()); + return status; + } + + /* + * remove all related objects from REDIS DB and also from existing + * object references since at this point they are no longer valid + */ + sai_object_id_t switchVid; + for (size_t idx = 0; idx < object_count; idx++) + { + if (statuses[idx] == SAI_STATUS_SUCCESS) + { + m_translator->eraseRidAndVid(objectRids[idx], objectVids[idx]); + + switchVid = VidManager::switchIdQuery(objectVids[idx]); + + if (m_switches.at(switchVid)->isDiscoveredRid(objectRids[idx])) + { + m_switches.at(switchVid)->removeExistingObjectReference(objectRids[idx]); + } + + if (objectType == SAI_OBJECT_TYPE_PORT) + { + m_switches.at(switchVid)->postPortRemove(objectRids[idx]); + } + } + } + + return status; +} + +sai_status_t Syncd::processBulkOid( + _In_ sai_object_type_t objectType, + _In_ const std::vector& objectIds, + _In_ sai_common_api_t api, + _In_ const std::vector>& attributes, + _In_ const std::vector>& strAttributes) +{ + SWSS_LOG_ENTER(); + + auto info = sai_metadata_get_object_type_info(objectType); + + if (info->isnonobjectid) + { + SWSS_LOG_THROW("passing non object id to bulk oid object operation"); + } + + std::vector statuses(objectIds.size()); + + sai_status_t all = SAI_STATUS_SUCCESS; + + if (m_commandLineOptions->m_enableSaiBulkSupport) + { + sai_bulk_op_error_mode_t mode = SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR; + + switch (api) + { + case SAI_COMMON_API_BULK_CREATE: + all = processBulkOidCreate(objectType, mode, objectIds, attributes, statuses); + break; + + case SAI_COMMON_API_BULK_SET: + all = processBulkOidSet(objectType, mode, objectIds, attributes, statuses); + break; + + case SAI_COMMON_API_BULK_GET: + all = processBulkOidGet(objectType, mode, objectIds, attributes, statuses); + break; + + case SAI_COMMON_API_BULK_REMOVE: + all = processBulkOidRemove(objectType, mode, objectIds, statuses); + break; + + default: + all = SAI_STATUS_NOT_SUPPORTED; + SWSS_LOG_ERROR("api %s is not supported in bulk mode", sai_serialize_common_api(api).c_str()); + } + + if (all != SAI_STATUS_NOT_SUPPORTED && all != SAI_STATUS_NOT_IMPLEMENTED) + { + switch (api) + { + case SAI_COMMON_API_BULK_GET: + sendBulkGetResponse(objectType, objectIds, all, attributes, statuses); + break; + default: + sendApiResponse(api, all, (uint32_t)objectIds.size(), statuses.data()); + break; + } + + syncUpdateRedisBulkQuadEvent(api, statuses, objectType, objectIds, strAttributes); + return all; + } + } + + // vendor SAI don't bulk API yet, so execute one by one + + all = SAI_STATUS_SUCCESS; + + for (size_t idx = 0; idx < objectIds.size(); ++idx) + { + sai_status_t status = SAI_STATUS_FAILURE; + + auto& list = attributes[idx]; + + sai_attribute_t *attr_list = list->get_attr_list(); + uint32_t attr_count = list->get_attr_count(); + + if (api == SAI_COMMON_API_BULK_CREATE) + { + status = processOid(objectType, objectIds[idx], SAI_COMMON_API_CREATE, attr_count, attr_list); + } + else if (api == SAI_COMMON_API_BULK_REMOVE) + { + status = processOid(objectType, objectIds[idx], SAI_COMMON_API_REMOVE, attr_count, attr_list); + } + else if (api == SAI_COMMON_API_BULK_SET) + { + status = processOid(objectType, objectIds[idx], SAI_COMMON_API_SET, attr_count, attr_list); + } + else if (api == SAI_COMMON_API_BULK_GET) + { + status = processOid(objectType, objectIds[idx], SAI_COMMON_API_GET, attr_count, attr_list); + } + else + { + SWSS_LOG_THROW("api %s is not supported in bulk mode", + sai_serialize_common_api(api).c_str()); + } + + if (status != SAI_STATUS_SUCCESS) + { + if (!m_enableSyncMode) + { + SWSS_LOG_THROW("operation %s for %s failed in async mode!", + sai_serialize_common_api(api).c_str(), + sai_serialize_object_type(objectType).c_str()); + } + + all = SAI_STATUS_FAILURE; // all can be success if all has been success + } + + statuses[idx] = status; + } + + switch (api) + { + case SAI_COMMON_API_BULK_GET: + sendBulkGetResponse(objectType, objectIds, all, attributes, statuses); + break; + default: + sendApiResponse(api, all, (uint32_t)objectIds.size(), statuses.data()); + break; + } + + syncUpdateRedisBulkQuadEvent(api, statuses, objectType, objectIds, strAttributes); + + return all; +} + +sai_status_t Syncd::processQuadEventInInitViewMode( + _In_ sai_object_type_t objectType, + _In_ const std::string& strObjectId, + _In_ sai_common_api_t api, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + /* + * Since attributes are not checked, it may happen that user will send some + * invalid VID in object id/list in attribute, metadata should handle that, + * but if that happen, this id will be treated as "new" object instead of + * existing one. + */ + + switch (api) + { + case SAI_COMMON_API_CREATE: + return processQuadInInitViewModeCreate(objectType, strObjectId, attr_count, attr_list); + + case SAI_COMMON_API_REMOVE: + return processQuadInInitViewModeRemove(objectType, strObjectId); + + case SAI_COMMON_API_SET: + return processQuadInInitViewModeSet(objectType, strObjectId, attr_list); + + case SAI_COMMON_API_GET: + return processQuadInInitViewModeGet(objectType, strObjectId, attr_count, attr_list); + + default: + + SWSS_LOG_THROW("common api (%s) is not implemented in init view mode", sai_serialize_common_api(api).c_str()); + } +} + +sai_status_t Syncd::processQuadInInitViewModeCreate( + _In_ sai_object_type_t objectType, + _In_ const std::string& strObjectId, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + if (objectType == SAI_OBJECT_TYPE_PORT) + { + /* + * Reason for this is that if user will create port, new port is not + * actually created so when for example querying new queues for new + * created port, there are not there, since no actual port create was + * issued on the ASIC. + */ + + SWSS_LOG_THROW("port object can't be created in init view mode"); + } + + auto info = sai_metadata_get_object_type_info(objectType); + + // we assume create of those non object id object types will succeed + + if (info->isobjectid) + { + sai_object_id_t objectVid; + sai_deserialize_object_id(strObjectId, objectVid); + + /* + * Object ID here is actual VID returned from redis during + * creation this is floating VID in init view mode. + */ + + SWSS_LOG_DEBUG("generic create (init view) for %s, floating VID: %s", + sai_serialize_object_type(objectType).c_str(), + sai_serialize_object_id(objectVid).c_str()); + + if (objectType == SAI_OBJECT_TYPE_SWITCH) + { + onSwitchCreateInInitViewMode(objectVid, attr_count, attr_list); + } + else + { + // in init view mode insert every created object except switch + + m_createdInInitView.insert(objectVid); + } + } + + sendApiResponse(SAI_COMMON_API_CREATE, SAI_STATUS_SUCCESS); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t Syncd::processQuadInInitViewModeRemove( + _In_ sai_object_type_t objectType, + _In_ const std::string& strObjectId) +{ + SWSS_LOG_ENTER(); + + if (objectType == SAI_OBJECT_TYPE_PORT) + { + /* + * Reason for this is that if user will remove port, actual resources + * for it won't be released, lanes would be still occupied and there is + * extra logic required in post port remove which clears OIDs + * (ipgs,queues,SGs) from redis db that are automatically removed by + * vendor SAI, and comparison logic don't support that. + */ + + SWSS_LOG_THROW("port object (%s) can't be removed in init view mode", strObjectId.c_str()); + } + + if (objectType == SAI_OBJECT_TYPE_SWITCH) + { + /* + * NOTE: Special care needs to be taken to clear all this switch id's + * from all db's currently we skip this since we assume that orchagent + * will not be removing switches, just creating. But it may happen + * when asic will fail etc. + * + * To support multiple switches this case must be refactored. + */ + + SWSS_LOG_THROW("remove switch (%s) is not supported in init view mode yet! FIXME", strObjectId.c_str()); + } + + // NOTE: we should also prevent removing some other non removable objects + + auto info = sai_metadata_get_object_type_info(objectType); + + if (info->isobjectid) + { + /* + * If object is existing object (like bridge port, vlan member) user + * may want to remove them, but this is temporary view, and when we + * receive apply view, we will populate existing objects to temporary + * view (since not all of them user may query) and this will produce + * conflict, since some of those objects user could explicitly remove. + * So to solve that we need to have a list of removed objects, and then + * only populate objects which not exist on removed list. + */ + + sai_object_id_t objectVid; + sai_deserialize_object_id(strObjectId, objectVid); + + // this set may contain removed objects from multiple switches + + m_initViewRemovedVidSet.insert(objectVid); + } + + sendApiResponse(SAI_COMMON_API_REMOVE, SAI_STATUS_SUCCESS); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t Syncd::processQuadInInitViewModeSet( + _In_ sai_object_type_t objectType, + _In_ const std::string& strObjectId, + _In_ sai_attribute_t *attr) +{ + SWSS_LOG_ENTER(); + + // we support SET api on all objects in init view mode + + sendApiResponse(SAI_COMMON_API_SET, SAI_STATUS_SUCCESS); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t Syncd::processQuadInInitViewModeGet( + _In_ sai_object_type_t objectType, + _In_ const std::string& strObjectId, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + + auto info = sai_metadata_get_object_type_info(objectType); + + sai_object_id_t switchVid = SAI_NULL_OBJECT_ID; + + if (info->isnonobjectid) + { + /* + * Those objects are user created, so if user created ROUTE he + * passed some attributes, there is no sense to support GET + * since user explicitly know what attributes were set, similar + * for other non object id types. + */ + + SWSS_LOG_ERROR("get is not supported on %s in init view mode", sai_serialize_object_type(objectType).c_str()); + + status = SAI_STATUS_NOT_SUPPORTED; + } + else + { + sai_object_id_t objectVid; + sai_deserialize_object_id(strObjectId, objectVid); + + if (isInitViewMode() && m_createdInInitView.find(objectVid) != m_createdInInitView.end()) + { + SWSS_LOG_WARN("GET api can't be used on %s (%s) since it's created in INIT_VIEW mode", + strObjectId.c_str(), + sai_serialize_object_type(objectType).c_str()); + + status = SAI_STATUS_INVALID_OBJECT_ID; + + sendGetResponse(objectType, strObjectId, switchVid, status, attr_count, attr_list); + + return status; + } + + switchVid = VidManager::switchIdQuery(objectVid); + + SWSS_LOG_DEBUG("generic get (init view) for object type %s:%s", + sai_serialize_object_type(objectType).c_str(), + strObjectId.c_str()); + + /* + * Object must exists, we can't call GET on created object + * in init view mode, get here can be called on existing + * objects like default trap group to get some vendor + * specific values. + * + * Exception here is switch, since all switches must be + * created, when user will create switch on init view mode, + * switch will be matched with existing switch, or it will + * be explicitly created so user can query it properties. + * + * Translate vid to rid will make sure that object exist + * and it have RID defined, so we can query it. + */ + + sai_object_id_t rid = m_translator->translateVidToRid(objectVid); + + sai_object_meta_key_t metaKey; + + metaKey.objecttype = objectType; + metaKey.objectkey.key.object_id = rid; + + status = m_vendorSai->get(metaKey, attr_count, attr_list); + } + + /* + * We are in init view mode, but ether switch already existed or first + * command was creating switch and user created switch. + * + * We could change that later on, depends on object type we can extract + * switch id, we could also have this method inside metadata to get meta + * key. + */ + + sendGetResponse(objectType, strObjectId, switchVid, status, attr_count, attr_list); + + return status; +} + +void Syncd::sendApiResponse( + _In_ sai_common_api_t api, + _In_ sai_status_t status, + _In_ uint32_t object_count, + _In_ sai_status_t* object_statuses) +{ + SWSS_LOG_ENTER(); + + /* + * By default synchronous mode is disabled and can be enabled by command + * line on syncd start. This will also require to enable synchronous mode + * in OA/sairedis because same GET RESPONSE channel is used to generate + * response for sairedis quad API. + */ + + if (!m_enableSyncMode) + { + return; + } + + switch (api) + { + case SAI_COMMON_API_CREATE: + case SAI_COMMON_API_REMOVE: + case SAI_COMMON_API_SET: + case SAI_COMMON_API_BULK_CREATE: + case SAI_COMMON_API_BULK_REMOVE: + case SAI_COMMON_API_BULK_SET: + break; + + default: + SWSS_LOG_THROW("api %s not supported by this function", + sai_serialize_common_api(api).c_str()); + } + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("api %s failed in syncd mode: %s", + sai_serialize_common_api(api).c_str(), + sai_serialize_status(status).c_str()); + } + + std::vector entry; + + for (uint32_t idx = 0; idx < object_count; idx++) + { + swss::FieldValueTuple fvt(sai_serialize_status(object_statuses[idx]), ""); + + entry.push_back(fvt); + } + + std::string strStatus = sai_serialize_status(status); + + SWSS_LOG_INFO("sending response for %s api with status: %s", + sai_serialize_common_api(api).c_str(), + strStatus.c_str()); + + m_selectableChannel->set(strStatus, entry, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); + + SWSS_LOG_INFO("response for %s api was send", + sai_serialize_common_api(api).c_str()); +} + +void Syncd::processFlexCounterGroupEvent( // TODO must be moved to go via ASIC channel queue + _In_ swss::ConsumerTable& consumer) +{ + SWSS_LOG_ENTER(); + + std::lock_guard lock(m_mutex); + + swss::KeyOpFieldsValuesTuple kco; + + consumer.pop(kco); + + auto& groupName = kfvKey(kco); + auto& op = kfvOp(kco); + auto& values = kfvFieldsValues(kco); + + WatchdogScope ws(m_timerWatchdog, op + ":" + groupName, &kco); + + processFlexCounterGroupEvent(groupName, op, values, false); +} + +sai_status_t Syncd::processFlexCounterGroupEvent( + _In_ const std::string &groupName, + _In_ const std::string &op, + _In_ const std::vector &values, + _In_ bool fromAsicChannel) +{ + SWSS_LOG_ENTER(); + + if (op == SET_COMMAND) + { + m_manager->addCounterPlugin(groupName, values); + if (fromAsicChannel) + { + m_flexCounterGroupTable->set(groupName, values); + } + } + else if (op == DEL_COMMAND) + { + if (fromAsicChannel) + { + m_flexCounterGroupTable->del(groupName); + } + m_manager->removeCounterPlugins(groupName); + } + else + { + SWSS_LOG_ERROR("unknown command: %s", op.c_str()); + } + + if (fromAsicChannel) + { + sendApiResponse(SAI_COMMON_API_SET, SAI_STATUS_SUCCESS); + } + + return SAI_STATUS_SUCCESS; +} + +void Syncd::processFlexCounterEvent( // TODO must be moved to go via ASIC channel queue + _In_ swss::ConsumerTable& consumer) +{ + SWSS_LOG_ENTER(); + + std::lock_guard lock(m_mutex); + + swss::KeyOpFieldsValuesTuple kco; + + consumer.pop(kco); + + auto& key = kfvKey(kco); + auto& op = kfvOp(kco); + auto& values = kfvFieldsValues(kco); + + WatchdogScope ws(m_timerWatchdog, op + ":" + key, &kco); + + processFlexCounterEvent(key, op, values, false); +} + +sai_status_t Syncd::processFlexCounterEvent( + _In_ const std::string &key, + _In_ const std::string &op, + _In_ const std::vector &values, + _In_ bool fromAsicChannel) +{ + SWSS_LOG_ENTER(); + + auto delimiter = key.find_first_of(":"); + + if (delimiter == std::string::npos) + { + SWSS_LOG_ERROR("Failed to parse the key %s", key.c_str()); + + if (fromAsicChannel) + { + sendApiResponse(SAI_COMMON_API_SET, SAI_STATUS_FAILURE); + } + + return SAI_STATUS_FAILURE; // if key is invalid there is no need to process this event again + } + + auto groupName = key.substr(0, delimiter); + auto strVids = key.substr(delimiter + 1); + auto vidStringVector = swss::tokenize(strVids, ','); + + if (fromAsicChannel && op == SET_COMMAND && (!vidStringVector.empty())) + { + std::vector vids; + std::vector rids; + std::vector keys; + + vids.reserve(vidStringVector.size()); + rids.reserve(vidStringVector.size()); + keys.reserve(vidStringVector.size()); + + for (auto &strVid: vidStringVector) + { + sai_object_id_t vid, rid; + sai_deserialize_object_id(strVid, vid); + vids.emplace_back(vid); + + if (!m_translator->tryTranslateVidToRid(vid, rid)) + { + SWSS_LOG_ERROR("port VID %s, was not found (probably port was removed/splitted) and will remove from counters now", + sai_serialize_object_id(vid).c_str()); + } + + rids.emplace_back(rid); + keys.emplace_back(groupName + ":" + strVid); + } + + m_manager->bulkAddCounter(vids, rids, groupName, values); + + for (auto &singleKey: keys) + { + m_flexCounterTable->set(singleKey, values); + } + + if (fromAsicChannel) + { + sendApiResponse(SAI_COMMON_API_SET, SAI_STATUS_SUCCESS); + } + + return SAI_STATUS_SUCCESS; + } + + for(auto &strVid : vidStringVector) + { + auto effective_op = op; + auto singleKey = groupName + ":" + strVid; + + sai_object_id_t vid; + sai_deserialize_object_id(strVid, vid); + + sai_object_id_t rid; + + if (!m_translator->tryTranslateVidToRid(vid, rid)) + { + if (fromAsicChannel) + { + SWSS_LOG_ERROR("port VID %s, was not found (probably port was removed/splitted) and will remove from counters now", + sai_serialize_object_id(vid).c_str()); + } + else + { + SWSS_LOG_WARN("port VID %s, was not found (probably port was removed/splitted) and will remove from counters now", + sai_serialize_object_id(vid).c_str()); + } + effective_op = DEL_COMMAND; + } + + if (effective_op == SET_COMMAND) + { + m_manager->addCounter(vid, rid, groupName, values); + if (fromAsicChannel) + { + m_flexCounterTable->set(singleKey, values); + } + } + else if (effective_op == DEL_COMMAND) + { + if (fromAsicChannel) + { + m_flexCounterTable->del(singleKey); + } + m_manager->removeCounter(vid, groupName); + } + else + { + SWSS_LOG_ERROR("unknown command: %s", op.c_str()); + } + } + + if (fromAsicChannel) + { + sendApiResponse(SAI_COMMON_API_SET, SAI_STATUS_SUCCESS); + } + + return SAI_STATUS_SUCCESS; +} + +void Syncd::syncUpdateRedisQuadEvent( + _In_ sai_status_t status, + _In_ sai_common_api_t api, + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + if (!m_enableSyncMode) + { + return; + } + + if (status != SAI_STATUS_SUCCESS) + { + return; + } + + // When in synchronous mode, we need to modify redis database when status + // is success, since consumer table on synchronous mode is not making redis + // changes and we only want to apply changes when api succeeded. This + // applies to init view mode and apply view mode. + + const std::string& key = kfvKey(kco); + + auto& values = kfvFieldsValues(kco); + + sai_object_meta_key_t metaKey; + sai_deserialize_object_meta_key(key, metaKey); + + const bool initView = isInitViewMode(); + + static PerformanceIntervalTimer timer("Syncd::syncUpdateRedisQuadEvent"); + + timer.start(); + + switch (api) + { + case SAI_COMMON_API_CREATE: + + { + if (initView) + m_client->createTempAsicObject(metaKey, values); + else + m_client->createAsicObject(metaKey, values); + + break; + } + + case SAI_COMMON_API_REMOVE: + + { + if (initView) + m_client->removeTempAsicObject(metaKey); + else + m_client->removeAsicObject(metaKey); + + break; + } + + case SAI_COMMON_API_SET: + + { + auto& first = values.at(0); + + auto& attr = fvField(first); + auto& value = fvValue(first); + + if (initView) + m_client->setTempAsicObject(metaKey, attr, value); + else + m_client->setAsicObject(metaKey, attr, value); + + break; + } + + case SAI_COMMON_API_GET: + break; // ignore get since get is not modifying db + + default: + + SWSS_LOG_THROW("api %d is not supported", api); + } + + timer.stop(); + + timer.inc(); +} + +void Syncd::syncUpdateRedisBulkQuadEvent( + _In_ sai_common_api_t api, + _In_ const std::vector& statuses, + _In_ sai_object_type_t objectType, + _In_ const std::vector& objectIds, + _In_ const std::vector>& strAttributes) +{ + SWSS_LOG_ENTER(); + + if (!m_enableSyncMode) + { + return; + } + + // When in synchronous mode, we need to modify redis database when status + // is success, since consumer table on synchronous mode is not making redis + // changes and we only want to apply changes when api succeeded. This + // applies to init view mode and apply view mode. + + static PerformanceIntervalTimer timer("Syncd::syncUpdateRedisBulkQuadEvent"); + + timer.start(); + + const std::string strObjectType = sai_serialize_object_type(objectType); + + std::unordered_map> multiHash; + + std::vector keys; + + for (size_t idx = 0; idx < statuses.size(); idx++) + { + sai_status_t status = statuses[idx]; + + if (status != SAI_STATUS_SUCCESS) + { + // in case of failure, don't modify database + continue; + } + + auto key = strObjectType + ":" + objectIds.at(idx); + + keys.push_back(key); + + if (api == SAI_COMMON_API_BULK_SET) + { + // in case of bulk set operation, it can happen that multiple + // attributes will be set for the same key, then when we want to + // push them to redis database, we need to combine all attributes + // to a single vector of attributes + + multiHash[key].push_back(strAttributes.at(idx).at(0)); + } + else + { + multiHash[key] = strAttributes.at(idx); + } + } + + const bool initView = isInitViewMode(); + + switch (api) + { + case SAI_COMMON_API_BULK_CREATE: + + { + if (initView) + m_client->createTempAsicObjects(multiHash); + else + m_client->createAsicObjects(multiHash); + + break; + } + + case SAI_COMMON_API_BULK_REMOVE: + + { + if (initView) + m_client->removeTempAsicObjects(keys); + else + m_client->removeAsicObjects(keys); + + break; + } + + case SAI_COMMON_API_BULK_SET: + + { + // SET is the same as create + if (initView) + m_client->createTempAsicObjects(multiHash); + else + m_client->createAsicObjects(multiHash); + + break; + } + + case SAI_COMMON_API_BULK_GET: + break; // ignore get since get is not modifying db + + default: + + SWSS_LOG_THROW("api %d is not supported", api); + } + + timer.stop(); + + timer.inc(statuses.size()); +} + +sai_status_t Syncd::processQuadEvent( + _In_ sai_common_api_t api, + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + const std::string& key = kfvKey(kco); + const std::string& op = kfvOp(kco); + + const std::string& strObjectId = key.substr(key.find(":") + 1); + + sai_object_meta_key_t metaKey; + sai_deserialize_object_meta_key(key, metaKey); + + if (!sai_metadata_is_object_type_valid(metaKey.objecttype)) + { + SWSS_LOG_THROW("invalid object type %s", key.c_str()); + } + + auto& values = kfvFieldsValues(kco); + + for (auto& v: values) + { + SWSS_LOG_DEBUG("attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str()); + } + + SaiAttributeList list(metaKey.objecttype, values, false); + + /* + * Attribute list can't be const since we will use it to translate VID to + * RID in place. + */ + + sai_attribute_t *attr_list = list.get_attr_list(); + uint32_t attr_count = list.get_attr_count(); + + /* + * NOTE: This check pointers must be executed before init view mode, since + * this methods replaces pointers from orchagent memory space to syncd + * memory space. + */ + + if (metaKey.objecttype == SAI_OBJECT_TYPE_SWITCH && (api == SAI_COMMON_API_CREATE || api == SAI_COMMON_API_SET)) + { + /* + * We don't need to clear those pointers on switch remove (even last), + * since those pointers will reside inside attributes, also sairedis + * will internally check whether pointer is null or not, so we here + * will receive all notifications, but redis only those that were set. + * + * TODO: must be done per switch, and switch may not exists yet + */ + + m_handler->updateNotificationsPointers(attr_count, attr_list); + } + + if (isInitViewMode()) + { + sai_status_t status = processQuadEventInInitViewMode(metaKey.objecttype, strObjectId, api, attr_count, attr_list); + + syncUpdateRedisQuadEvent(status, api, kco); + + return status; + } + + if (api != SAI_COMMON_API_GET) + { + /* + * NOTE: we can also call translate on get, if sairedis will clean + * buffer so then all OIDs will be NULL, and translation will also + * convert them to NULL. + */ + + SWSS_LOG_DEBUG("translating VID to RIDs on all attributes"); + + m_translator->translateVidToRid(metaKey.objecttype, attr_count, attr_list); + } + + auto info = sai_metadata_get_object_type_info(metaKey.objecttype); + + sai_status_t status; + + if (info->isnonobjectid) + { + if (info->objecttype == SAI_OBJECT_TYPE_ROUTE_ENTRY) + { + static PerformanceIntervalTimer timer("Syncd::processQuadEvent::processEntry(route_entry)"); + + timer.start(); + + status = processEntry(metaKey, api, attr_count, attr_list); + + timer.stop(); + + timer.inc(); + } + else + { + status = processEntry(metaKey, api, attr_count, attr_list); + } + } + else + { + status = processOid(metaKey.objecttype, strObjectId, api, attr_count, attr_list); + } + + if (api == SAI_COMMON_API_GET) + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_INFO("get API for key: %s op: %s returned status: %s", + key.c_str(), + op.c_str(), + sai_serialize_status(status).c_str()); + } + + // extract switch VID from any object type + + sai_object_id_t switchVid = VidManager::switchIdQuery(metaKey.objectkey.key.object_id); + + sendGetResponse(metaKey.objecttype, strObjectId, switchVid, status, attr_count, attr_list); + } + else if (status != SAI_STATUS_SUCCESS) + { + sendApiResponse(api, status); + + if (info->isobjectid && api == SAI_COMMON_API_SET) + { + sai_object_id_t vid = metaKey.objectkey.key.object_id; + sai_object_id_t rid = m_translator->translateVidToRid(vid); + + SWSS_LOG_ERROR("VID: %s RID: %s", + sai_serialize_object_id(vid).c_str(), + sai_serialize_object_id(rid).c_str()); + } + + for (const auto &v: values) + { + SWSS_LOG_ERROR("attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str()); + } + + if (!m_enableSyncMode) + { + // throw only when sync mode is not enabled + + SWSS_LOG_THROW("failed to execute api: %s, key: %s, status: %s", + op.c_str(), + key.c_str(), + sai_serialize_status(status).c_str()); + } + } + else // non GET api, status is SUCCESS + { + sendApiResponse(api, status); + } + + syncUpdateRedisQuadEvent(status, api, kco); + + return status; +} + +sai_status_t Syncd::processOid( + _In_ sai_object_type_t objectType, + _In_ const std::string &strObjectId, + _In_ sai_common_api_t api, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t object_id; + sai_deserialize_object_id(strObjectId, object_id); + + SWSS_LOG_DEBUG("calling %s for %s", + sai_serialize_common_api(api).c_str(), + sai_serialize_object_type(objectType).c_str()); + + /* + * We need to do translate vid/rid except for create, since create will + * create new RID value, and we will have to map them to VID we received in + * create query. + */ + + auto info = sai_metadata_get_object_type_info(objectType); + + if (info->isnonobjectid) + { + SWSS_LOG_THROW("passing non object id %s as generic object", info->objecttypename); + } + + switch (api) + { + case SAI_COMMON_API_CREATE: + return processOidCreate(objectType, strObjectId, attr_count, attr_list); + + case SAI_COMMON_API_REMOVE: + return processOidRemove(objectType, strObjectId); + + case SAI_COMMON_API_SET: + return processOidSet(objectType, strObjectId, attr_list); + + case SAI_COMMON_API_GET: + return processOidGet(objectType, strObjectId, attr_count, attr_list); + + default: + + SWSS_LOG_THROW("common api (%s) is not implemented", sai_serialize_common_api(api).c_str()); + } +} + +sai_status_t Syncd::processOidCreate( + _In_ sai_object_type_t objectType, + _In_ const std::string &strObjectId, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t objectVid; + sai_deserialize_object_id(strObjectId, objectVid); + + // Object id is VID, we can use it to extract switch id. + + sai_object_id_t switchVid = VidManager::switchIdQuery(objectVid); + + sai_object_id_t switchRid = SAI_NULL_OBJECT_ID; + + if (objectType == SAI_OBJECT_TYPE_SWITCH) + { + SWSS_LOG_NOTICE("creating switch number %zu", m_switches.size() + 1); + } + else + { + /* + * When we are creating switch, then switchId parameter is ignored, but + * we can't convert it using vid to rid map, since rid doesn't exist + * yet, so skip translate for switch, but use translate for all other + * objects. + */ + + switchRid = m_translator->translateVidToRid(switchVid); + } + + sai_object_id_t objectRid; + + sai_status_t status = m_vendorSai->create(objectType, &objectRid, switchRid, attr_count, attr_list); + + if (status == SAI_STATUS_SUCCESS) + { + /* + * Object was created so new object id was generated we need to save + * virtual id's to redis db. + */ + + m_translator->insertRidAndVid(objectRid, objectVid); + + SWSS_LOG_INFO("saved VID %s to RID %s", + sai_serialize_object_id(objectVid).c_str(), + sai_serialize_object_id(objectRid).c_str()); + + if (objectType == SAI_OBJECT_TYPE_SWITCH) + { + /* + * All needed data to populate switch should be obtained inside SaiSwitch + * constructor, like getting all queues, ports, etc. + */ + + m_switches[switchVid] = std::make_shared(switchVid, objectRid, m_client, m_translator, m_vendorSai, false); + + m_mdioIpcServer->setSwitchId(objectRid); + + startDiagShell(objectRid); + } + + if (objectType == SAI_OBJECT_TYPE_PORT) + { + m_switches.at(switchVid)->onPostPortsCreate(1, &objectRid); + } + } + + return status; +} + +sai_status_t Syncd::processOidRemove( + _In_ sai_object_type_t objectType, + _In_ const std::string &strObjectId) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t objectVid; + sai_deserialize_object_id(strObjectId, objectVid); + + sai_object_id_t rid = m_translator->translateVidToRid(objectVid); + + if (objectType == SAI_OBJECT_TYPE_PORT) + { + sai_object_id_t switchVid = VidManager::switchIdQuery(objectVid); + + m_switches.at(switchVid)->collectPortRelatedObjects(rid); + } + + sai_status_t status = m_vendorSai->remove(objectType, rid); + + if (status == SAI_STATUS_SUCCESS) + { + // remove all related objects from REDIS DB and also from existing + // object references since at this point they are no longer valid + + m_translator->eraseRidAndVid(rid, objectVid); + + if (objectType == SAI_OBJECT_TYPE_SWITCH) + { + /* + * On remove switch there should be extra action all local objects + * and redis object should be removed on remove switch local and + * redis db objects should be cleared. + * + * Currently we don't want to remove switch so we don't need this + * method, but lets put this as a safety check. + */ + + SWSS_LOG_THROW("remove switch is not implemented, FIXME"); + } + else + { + /* + * Removing some object succeeded. Let's check if that + * object was default created object, eg. vlan member. + * Then we need to update default created object map in + * SaiSwitch to be in sync, and be prepared for apply + * view to transfer those synced default created + * objects to temporary view when it will be created, + * since that will be out basic switch state. + * + * TODO: there can be some issues with reference count + * like for schedulers on scheduler groups since they + * should have internal references, and we still need + * to create dependency tree from saiDiscovery and + * update those references to track them, this is + * printed in metadata sanitycheck as "default value + * needs to be stored". + * + * TODO lets add SAI metadata flag for that this will + * also needs to be of internal/vendor default but we + * can already deduce that. + */ + + sai_object_id_t switchVid = VidManager::switchIdQuery(objectVid); + + if (m_switches.at(switchVid)->isDiscoveredRid(rid)) + { + m_switches.at(switchVid)->removeExistingObjectReference(rid); + } + + if (objectType == SAI_OBJECT_TYPE_PORT) + { + m_switches.at(switchVid)->postPortRemove(rid); + } + } + } + + return status; +} + +sai_status_t Syncd::processOidSet( + _In_ sai_object_type_t objectType, + _In_ const std::string &strObjectId, + _In_ sai_attribute_t *attr) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t objectVid; + sai_deserialize_object_id(strObjectId, objectVid); + + sai_object_id_t rid = m_translator->translateVidToRid(objectVid); + + sai_status_t status = m_vendorSai->set(objectType, rid, attr); + + if (Workaround::isSetAttributeWorkaround(objectType, attr->id, status)) + { + return SAI_STATUS_SUCCESS; + } + + return status; +} + +sai_status_t Syncd::processOidGet( + _In_ sai_object_type_t objectType, + _In_ const std::string &strObjectId, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t objectVid; + sai_deserialize_object_id(strObjectId, objectVid); + + sai_object_id_t rid = m_translator->translateVidToRid(objectVid); + + return m_vendorSai->get(objectType, rid, attr_count, attr_list); +} + +const char* Syncd::profileGetValue( + _In_ sai_switch_profile_id_t profile_id, + _In_ const char* variable) +{ + SWSS_LOG_ENTER(); + + if (variable == NULL) + { + SWSS_LOG_WARN("variable is null"); + return NULL; + } + + auto it = m_profileMap.find(variable); + + if (it == m_profileMap.end()) + { + SWSS_LOG_NOTICE("%s: NULL", variable); + return NULL; + } + + SWSS_LOG_NOTICE("%s: %s", variable, it->second.c_str()); + + return it->second.c_str(); +} + +int Syncd::profileGetNextValue( + _In_ sai_switch_profile_id_t profile_id, + _Out_ const char** variable, + _Out_ const char** value) +{ + SWSS_LOG_ENTER(); + + if (value == NULL) + { + SWSS_LOG_INFO("resetting profile map iterator"); + + m_profileIter = m_profileMap.begin(); + return 0; + } + + if (variable == NULL) + { + SWSS_LOG_WARN("variable is null"); + return -1; + } + + if (m_profileIter == m_profileMap.end()) + { + SWSS_LOG_INFO("iterator reached end"); + return -1; + } + + *variable = m_profileIter->first.c_str(); + *value = m_profileIter->second.c_str(); + + SWSS_LOG_INFO("key: %s:%s", *variable, *value); + + m_profileIter++; + + return 0; +} + +void Syncd::loadProfileMap() +{ + SWSS_LOG_ENTER(); + + // in case of virtual switch, populate context config + m_profileMap[SAI_KEY_VS_GLOBAL_CONTEXT] = std::to_string(m_commandLineOptions->m_globalContext); + m_profileMap[SAI_KEY_VS_CONTEXT_CONFIG] = m_commandLineOptions->m_contextConfig; + + if (m_commandLineOptions->m_profileMapFile.size() == 0) + { + SWSS_LOG_NOTICE("profile map file not specified"); + return; + } + + std::ifstream profile(m_commandLineOptions->m_profileMapFile); + + if (!profile.is_open()) + { + SWSS_LOG_ERROR("failed to open profile map file: %s: %s", + m_commandLineOptions->m_profileMapFile.c_str(), + strerror(errno)); + + exit(EXIT_FAILURE); + } + + // Provide default value at boot up time and let sai profile value + // Override following values if existing. + // SAI reads these values at start up time. It would be too late to + // set these values later when WARM BOOT is detected. + + m_profileMap[SAI_KEY_WARM_BOOT_WRITE_FILE] = DEF_SAI_WARM_BOOT_DATA_FILE; + m_profileMap[SAI_KEY_WARM_BOOT_READ_FILE] = DEF_SAI_WARM_BOOT_DATA_FILE; + + std::string line; + + while (getline(profile, line)) + { + if (line.size() > 0 && (line[0] == '#' || line[0] == ';')) + { + continue; + } + + size_t pos = line.find("="); + + if (pos == std::string::npos) + { + SWSS_LOG_WARN("not found '=' in line %s", line.c_str()); + continue; + } + + std::string key = line.substr(0, pos); + std::string value = line.substr(pos + 1); + + m_profileMap[key] = value; + + SWSS_LOG_INFO("insert: %s:%s", key.c_str(), value.c_str()); + } +} + +void Syncd::sendGetResponse( + _In_ sai_object_type_t objectType, + _In_ const std::string& strObjectId, + _In_ sai_object_id_t switchVid, + _In_ sai_status_t status, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + std::vector entry; + + if (status == SAI_STATUS_SUCCESS) + { + m_translator->translateRidToVid(objectType, switchVid, attr_count, attr_list); + + /* + * Normal serialization + translate RID to VID. + */ + + entry = SaiAttributeList::serialize_attr_list( + objectType, + attr_count, + attr_list, + false); + + /* + * All oid values here are VIDs. + */ + + snoopGetResponse(objectType, strObjectId, attr_count, attr_list); + } + else if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + /* + * In this case we got correct values for list, but list was too small + * so serialize only count without list itself, sairedis will need to + * take this into account when deserialize. + * + * If there was a list somewhere, count will be changed to actual value + * different attributes can have different lists, many of them may + * serialize only count, and will need to support that on the receiver. + */ + + entry = SaiAttributeList::serialize_attr_list( + objectType, + attr_count, + attr_list, + true); + } + else + { + /* + * Some other error, don't send attributes at all. + */ + } + + for (const auto &e: entry) + { + SWSS_LOG_DEBUG("attr: %s: %s", fvField(e).c_str(), fvValue(e).c_str()); + } + + std::string strStatus = sai_serialize_status(status); + + SWSS_LOG_INFO("sending response for GET api with status: %s", strStatus.c_str()); + + /* + * Since we have only one get at a time, we don't have to serialize object + * type and object id, only get status is required to be returned. Get + * response will not put any data to table, only queue is used. + */ + + m_selectableChannel->set(strStatus, entry, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); + + SWSS_LOG_INFO("response for GET api was send"); +} + +void Syncd::sendBulkGetResponse( + _In_ sai_object_type_t objectType, + _In_ const std::vector& strObjectIds, + _In_ sai_status_t status, + _In_ const std::vector>& attributes, + _In_ const std::vector& statuses) +{ + SWSS_LOG_ENTER(); + + std::vector entries; + entries.reserve(strObjectIds.size()); + + for (uint32_t idx = 0; idx < strObjectIds.size(); idx++) + { + const auto objectStatus = statuses[idx]; + const auto objectStatusStr = sai_serialize_status(statuses[idx]); + + if (objectStatus == SAI_STATUS_SUCCESS) + { + sai_object_id_t objectId{}; + sai_deserialize_object_id(strObjectIds[idx], objectId); + const auto switchVid = VidManager::switchIdQuery(objectId); + m_translator->translateRidToVid(objectType, switchVid, attributes[idx]->get_attr_count(), attributes[idx]->get_attr_list()); + + const auto entry = SaiAttributeList::serialize_attr_list(objectType, attributes[idx]->get_attr_count(), attributes[idx]->get_attr_list(), false); + const auto joined = Globals::joinFieldValues(entry); + + // Object IDs are not serialized. The attributes are assumed to be in order the object IDs were passed. + // Essentially, only status and attribute list is needed to be serialized and sent. + swss::FieldValueTuple fvt(objectStatusStr, joined); + + entries.push_back(fvt); + + /* + * All oid values here are VIDs. + */ + + snoopGetResponse(objectType, strObjectIds[idx], attributes[idx]->get_attr_count(), attributes[idx]->get_attr_list()); + } + else if (objectStatus == SAI_STATUS_BUFFER_OVERFLOW) + { + const auto entry = SaiAttributeList::serialize_attr_list(objectType, attributes[idx]->get_attr_count(), attributes[idx]->get_attr_list(), true); + const auto joined = Globals::joinFieldValues(entry); + + swss::FieldValueTuple fvt(objectStatusStr, joined); + + entries.push_back(fvt); + } + else + { + swss::FieldValueTuple fvt(objectStatusStr, Globals::joinFieldValues({})); + + entries.push_back(fvt); + } + } + + for (const auto &e: entries) + { + SWSS_LOG_DEBUG("attr: %s: %s", fvField(e).c_str(), fvValue(e).c_str()); + } + + const auto strStatus = sai_serialize_status(status); + + SWSS_LOG_INFO("sending response for bulk GET api with status: %s", strStatus.c_str()); + + m_selectableChannel->set(strStatus, entries, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); + + SWSS_LOG_INFO("response for bulk GET api was send"); +} + +void Syncd::snoopGetResponse( + _In_ sai_object_type_t object_type, + _In_ const std::string& strObjectId, // can be non object id + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + /* + * NOTE: this method is operating on VIDs, all RIDs were translated outside + * this method. + */ + + /* + * Vlan (including vlan 1) will need to be put into TEMP view this should + * also be valid for all objects that were queried. + */ + + for (uint32_t idx = 0; idx < attr_count; ++idx) + { + const sai_attribute_t &attr = attr_list[idx]; + + auto meta = sai_metadata_get_attr_metadata(object_type, attr.id); + + if (meta == NULL) + { + SWSS_LOG_THROW("unable to get metadata for object type %d, attribute %d", object_type, attr.id); + } + + /* + * We should snoop oid values even if they are readonly we just note in + * temp view that those objects exist on switch. + */ + + switch (meta->attrvaluetype) + { + case SAI_ATTR_VALUE_TYPE_OBJECT_ID: + snoopGetOid(attr.value.oid); + break; + + case SAI_ATTR_VALUE_TYPE_OBJECT_LIST: + snoopGetOidList(attr.value.objlist); + break; + + case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID: + if (attr.value.aclfield.enable) + snoopGetOid(attr.value.aclfield.data.oid); + break; + + case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST: + if (attr.value.aclfield.enable) + snoopGetOidList(attr.value.aclfield.data.objlist); + break; + + case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_ID: + if (attr.value.aclaction.enable) + snoopGetOid(attr.value.aclaction.parameter.oid); + break; + + case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST: + if (attr.value.aclaction.enable) + snoopGetOidList(attr.value.aclaction.parameter.objlist); + break; + + default: + + /* + * If in future new attribute with object id will be added this + * will make sure that we will need to add handler here. + */ + + if (meta->isoidattribute) + { + SWSS_LOG_THROW("attribute %s is object id, but not processed, FIXME", meta->attridname); + } + + break; + } + + if (SAI_HAS_FLAG_READ_ONLY(meta->flags)) + { + /* + * If value is read only, we skip it, since after syncd restart we + * won't be able to set/create it anyway. + */ + + continue; + } + + if (meta->objecttype == SAI_OBJECT_TYPE_PORT && + meta->attrid == SAI_PORT_ATTR_HW_LANE_LIST) + { + /* + * Skip port lanes for now since we don't create ports. + */ + + SWSS_LOG_INFO("skipping %s for %s", meta->attridname, strObjectId.c_str()); + continue; + } + + /* + * Put non readonly, and non oid attribute value to temp view. + * + * NOTE: This will also put create-only attributes to view, and after + * syncd hard reinit we will not be able to do "SET" on that attribute. + * + * Similar action can happen when we will do this on asicSet during + * apply view. + */ + + snoopGetAttrValue(strObjectId, meta, attr); + } +} + +void Syncd::snoopGetAttr( + _In_ sai_object_type_t objectType, + _In_ const std::string& strObjectId, + _In_ const std::string& attrId, + _In_ const std::string& attrValue) +{ + SWSS_LOG_ENTER(); + + std::string mk = sai_serialize_object_type(objectType) + ":" + strObjectId; + + sai_object_meta_key_t metaKey; + sai_deserialize_object_meta_key(mk, metaKey); + + if (isInitViewMode()) + { + m_client->setTempAsicObject(metaKey, attrId, attrValue); + } + else + { + m_client->setAsicObject(metaKey, attrId, attrValue); + } +} + +void Syncd::snoopGetOid( + _In_ sai_object_id_t vid) +{ + SWSS_LOG_ENTER(); + + if (vid == SAI_NULL_OBJECT_ID) + { + // if snooped oid is NULL then we don't need take any action + return; + } + + /* + * Check if object was previously discovered on this switch, then no need to update ASIC_STATE. + */ + if (!isInitViewMode()) + { + sai_object_id_t rid; + + if (m_translator->tryTranslateVidToRid(vid, rid)) + { + const auto switchVid = VidManager::switchIdQuery(vid); + + if (m_switches[switchVid]->isDiscoveredRid(rid)) + { + // Already discovered object. + return; + } + } + } + + /* + * We need use redis version of object type query here since we are + * operating on VID value, and syncd is compiled against real SAI + * implementation which has different function m_vendorSai->objectTypeQuery. + */ + + sai_object_type_t objectType = VidManager::objectTypeQuery(vid); + + std::string strVid = sai_serialize_object_id(vid); + + snoopGetAttr(objectType, strVid, "NULL", "NULL"); +} + +void Syncd::snoopGetOidList( + _In_ const sai_object_list_t& list) +{ + SWSS_LOG_ENTER(); + + for (uint32_t i = 0; i < list.count; i++) + { + snoopGetOid(list.list[i]); + } +} + +void Syncd::snoopGetAttrValue( + _In_ const std::string& strObjectId, + _In_ const sai_attr_metadata_t *meta, + _In_ const sai_attribute_t& attr) +{ + SWSS_LOG_ENTER(); + + std::string value = sai_serialize_attr_value(*meta, attr); + + SWSS_LOG_DEBUG("%s:%s", meta->attridname, value.c_str()); + + snoopGetAttr(meta->objecttype, strObjectId, meta->attridname, value); +} + +void Syncd::inspectAsic() +{ + SWSS_LOG_ENTER(); + + // Fetch all the keys from ASIC DB + // Loop through all the keys in ASIC DB + + for (const auto &key: m_client->getAsicStateKeys()) + { + // ASIC_STATE:objecttype:objectid (object id may contain ':') + + auto start = key.find_first_of(":"); + + if (start == std::string::npos) + { + SWSS_LOG_ERROR("invalid ASIC_STATE_TABLE %s: no start :", key.c_str()); + break; + } + + auto mk = key.substr(start + 1); + + sai_object_meta_key_t metaKey; + sai_deserialize_object_meta_key(mk, metaKey); + + // Find all the attrid from ASIC DB, and use them to query ASIC + + auto hash = m_client->getAttributesFromAsicKey(key); + + std::vector values; + + for (auto &kv: hash) + { + const std::string &skey = kv.first; + const std::string &svalue = kv.second; + + swss::FieldValueTuple fvt(skey, svalue); + + values.push_back(fvt); + } + + SaiAttributeList list(metaKey.objecttype, values, false); + + sai_attribute_t *attr_list = list.get_attr_list(); + + uint32_t attr_count = list.get_attr_count(); + + SWSS_LOG_DEBUG("attr count: %u", list.get_attr_count()); + + if (attr_count == 0) + { + // TODO: how to check ASIC on ASIC DB key with NULL:NULL hash + // just ignore for now + continue; + } + + m_translator->translateVidToRid(metaKey); + + sai_status_t status = m_vendorSai->get(metaKey, attr_count, attr_list); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("failed to execute get api on %s: %s", + sai_serialize_object_meta_key(metaKey).c_str(), + sai_serialize_status(status).c_str()); + continue; + } + + SaiAttributeList redis_list(metaKey.objecttype, values, false); + + sai_attribute_t *redis_attr_list = redis_list.get_attr_list(); + + m_translator->translateVidToRid(metaKey.objecttype, attr_count, redis_attr_list); + + // compare fields and values from ASIC_DB and SAI response and log the difference + + for (uint32_t index = 0; index < attr_count; ++index) + { + const sai_attribute_t& attr = attr_list[index]; + + auto meta = sai_metadata_get_attr_metadata(metaKey.objecttype, attr.id); + + if (meta == NULL) + { + SWSS_LOG_ERROR("FATAL: failed to find metadata for object type %s and attr id %d", + sai_serialize_object_type(metaKey.objecttype).c_str(), + attr.id); + break; + } + + std::string strSaiAttrValue = sai_serialize_attr_value(*meta, attr, false); + + std::string strRedisAttrValue = sai_serialize_attr_value(*meta, redis_attr_list[index], false); + + if (strRedisAttrValue == strSaiAttrValue) + { + SWSS_LOG_INFO("matched %s REDIS and ASIC attr value '%s' with on %s", + meta->attridname, + strRedisAttrValue.c_str(), + sai_serialize_object_meta_key(metaKey).c_str()); + } + else + { + SWSS_LOG_ERROR("failed to match %s REDIS attr '%s' with ASIC attr '%s' for %s", + meta->attridname, + strRedisAttrValue.c_str(), + strSaiAttrValue.c_str(), + sai_serialize_object_meta_key(metaKey).c_str()); + } + } + } +} + +sai_status_t Syncd::processNotifySyncd( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + auto& key = kfvKey(kco); + sai_status_t status = SAI_STATUS_SUCCESS; + auto redisNotifySyncd = sai_deserialize_redis_notify_syncd(key); + + if (redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_INVOKE_DUMP) + { + SWSS_LOG_NOTICE("Invoking SAI failure dump"); + std::string ret_str; + int ret = swss::exec(SAI_FAILURE_DUMP_SCRIPT, ret_str); + if (ret != 0) + { + SWSS_LOG_ERROR("Error in executing SAI failure dump %s", ret_str.c_str()); + status = SAI_STATUS_FAILURE; + } + sendNotifyResponse(status); + return status; + } + + if (!m_commandLineOptions->m_enableTempView) + { + SWSS_LOG_NOTICE("received %s, ignored since TEMP VIEW is not used, returning success", key.c_str()); + + sendNotifyResponse(SAI_STATUS_SUCCESS); + + return SAI_STATUS_SUCCESS; + } + + if (m_veryFirstRun && m_firstInitWasPerformed && redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_INIT_VIEW) + { + /* + * Make sure that when second INIT view arrives, then we will jump to + * next section, since second init view may create switch that already + * exists and will fail with creating multiple switches error. + */ + + m_veryFirstRun = false; + } + else if (m_veryFirstRun) + { + SWSS_LOG_NOTICE("very first run is TRUE, op = %s", key.c_str()); + + + /* + * On the very first start of syncd, "compile" view is directly applied + * on device, since it will make it easier to switch to new asic state + * later on when we restart orch agent. + */ + + if (redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_INIT_VIEW) + { + /* + * On first start we just do "apply" directly on asic so we set + * init to false instead of true. + */ + + m_asicInitViewMode = false; + + m_firstInitWasPerformed = true; + + // we need to clear current temp view to make space for new one + + clearTempView(); + + /* + * Transition to longer watchdog timeout in INIT_VIEW on Chassis Switch + * Wait for create:SAI_OBJECT_TYPE_SWITCH + * Then transition back in APPLY_VIEW + */ + transitionToInitWatchdogTimeout(); + } + else if (redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_APPLY_VIEW) + { + m_veryFirstRun = false; + + m_asicInitViewMode = false; +#ifdef MELLANOX + bool applyViewInFastFastBoot = m_commandLineOptions->m_startType == SAI_START_TYPE_FASTFAST_BOOT || + m_commandLineOptions->m_startType == SAI_START_TYPE_EXPRESS_BOOT || + m_commandLineOptions->m_startType == SAI_START_TYPE_FAST_BOOT; +#else + bool applyViewInFastFastBoot = m_commandLineOptions->m_startType == SAI_START_TYPE_FASTFAST_BOOT || + m_commandLineOptions->m_startType == SAI_START_TYPE_EXPRESS_BOOT; +#endif + if (applyViewInFastFastBoot) + { + // express/fastfast boot configuration end + + status = onApplyViewInFastFastBoot(); + } + + SWSS_LOG_NOTICE("setting very first run to FALSE, op = %s", key.c_str()); + + transitionToNormalWatchdogTimeout(); + } + else if (redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_INSPECT_ASIC) + { + SWSS_LOG_NOTICE("syncd switched to INSPECT ASIC mode"); + + transitionToInitWatchdogTimeout(); + + inspectAsic(); + + transitionToNormalWatchdogTimeout(); + + sendNotifyResponse(SAI_STATUS_SUCCESS); + } + else + { + SWSS_LOG_THROW("unknown operation: %s", key.c_str()); + } + + sendNotifyResponse(status); + + return status; + } + + if (redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_INIT_VIEW) + { + if (m_asicInitViewMode) + { + SWSS_LOG_WARN("syncd is already in asic INIT VIEW mode, but received init again, orchagent restarted before apply?"); + } + + m_asicInitViewMode = true; + + clearTempView(); + + m_createdInInitView.clear(); + + // NOTE: Currently as WARN to be easier to spot, later should be NOTICE. + + SWSS_LOG_WARN("syncd switched to INIT VIEW mode, all op will be saved to TEMP view"); + + /* + * Transition to longer watchdog timeout in INIT_VIEW on Chassis Switch + * Wait for create:SAI_OBJECT_TYPE_SWITCH + * Then transition back in APPLY_VIEW + */ + transitionToInitWatchdogTimeout(); + + sendNotifyResponse(SAI_STATUS_SUCCESS); + } + else if (redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_APPLY_VIEW) + { + m_asicInitViewMode = false; + + // NOTE: Currently as WARN to be easier to spot, later should be NOTICE. + + SWSS_LOG_WARN("syncd received APPLY VIEW, will translate"); + + try + { + status = applyView(); + } + catch(...) + { + /* + * If apply view will fail with exception, try to send fail + * response to sairedis, since later there can be switch shutdown + * notification sent, and it will be synchronized with mutex, and + * it will not be processed until get response timeout will hit. + */ + + sendNotifyResponse(SAI_STATUS_FAILURE); + + throw; + } + + transitionToNormalWatchdogTimeout(); + + sendNotifyResponse(status); + + if (status == SAI_STATUS_SUCCESS) + { + /* + * We successfully applied new view, VID mapping could change, so + * we need to clear local db, and all new VIDs will be queried + * using redis. + * + * TODO possible race condition - get notification when new view is + * applied and cache have old values, and notification start's + * translating vid/rid, we need to stop processing notifications + * for transition (queue can still grow), possible fdb + * notifications but fdb learning was disabled on warm boot, so + * there should be no issue. + */ + + m_translator->clearLocalCache(); + + m_createdInInitView.clear(); + } + else + { + /* + * Apply view failed. It can fail in 2 ways, ether nothing was + * executed, on asic, or asic is inconsistent state then we should + * die or hang. + */ + + return status; + } + } + else if (redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_INSPECT_ASIC) + { + SWSS_LOG_NOTICE("syncd switched to INSPECT ASIC mode"); + + transitionToInitWatchdogTimeout(); + + inspectAsic(); + + transitionToNormalWatchdogTimeout(); + + sendNotifyResponse(SAI_STATUS_SUCCESS); + } + else + { + SWSS_LOG_ERROR("unknown operation: %s", key.c_str()); + + sendNotifyResponse(SAI_STATUS_NOT_IMPLEMENTED); + + SWSS_LOG_THROW("notify syncd %s operation failed", key.c_str()); + } + + return SAI_STATUS_SUCCESS; +} + +void Syncd::sendNotifyResponse( + _In_ sai_status_t status) +{ + SWSS_LOG_ENTER(); + + std::string strStatus = sai_serialize_status(status); + + std::vector entry; + + SWSS_LOG_INFO("sending response: %s", strStatus.c_str()); + + m_selectableChannel->set(strStatus, entry, REDIS_ASIC_STATE_COMMAND_NOTIFY); +} + +void Syncd::transitionToNormalWatchdogTimeout() +{ + SWSS_LOG_ENTER(); + + int64_t normalTimeout = m_commandLineOptions->m_watchdogWarnTimeSpan * WD_DELAY_FACTOR; + + m_timerWatchdog.setWarnTimespan(normalTimeout); +} + +void Syncd::transitionToInitWatchdogTimeout() +{ + SWSS_LOG_ENTER(); + + int64_t initTimeout = m_commandLineOptions->m_watchdogInitTimeSpan * WD_DELAY_FACTOR; + + m_timerWatchdog.setWarnTimespan(initTimeout); +} + +void Syncd::clearTempView() +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("clearing current TEMP VIEW"); + + SWSS_LOG_TIMER("clear temp view"); + + m_client->removeTempAsicStateTable(); + + // Also clear list of objects removed in init view mode. + + m_initViewRemovedVidSet.clear(); +} + +sai_status_t Syncd::onApplyViewInFastFastBoot() +{ + SWSS_LOG_ENTER(); + + sai_status_t all = SAI_STATUS_SUCCESS; + + for (auto& kvp: m_switches) + { + sai_attribute_t attr; + + attr.id = SAI_SWITCH_ATTR_FAST_API_ENABLE; + attr.value.booldata = false; + + sai_status_t status = m_vendorSai->set(SAI_OBJECT_TYPE_SWITCH, kvp.second->getRid(), &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_FAST_API_ENABLE=false: %s for switch RID: %s", + sai_serialize_status(status).c_str(), + sai_serialize_object_id(kvp.second->getRid()).c_str()); + + all = status; + } + } + + return all; +} + +sai_status_t Syncd::applyView() +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_TIMER("apply"); + + /* + * We assume that there will be no case that we will move from 1 to 0, also + * if at the beginning there is no switch, then when user will send create, + * and it will be actually created (real call) so there should be no case + * when we are moving from 0 -> 1. + */ + + /* + * This method contains 2 stages. + * + * First stage is non destructive, when orchagent will build new view, and + * there will be bug in comparison logic in first stage, then syncd will + * send failure when doing apply view to orchagent but it will still be + * running. No asic operations are performed during this stage. + * + * Second stage is destructive, so if there will be bug in comparison logic + * or any asic operation will fail, then syncd will crash, since asic will + * be in inconsistent state. + */ + + /* + * Initialize rand for future candidate object selection if necessary. + * + * NOTE: Should this be deterministic? So we could repeat random choice + * when something bad happen or we hit a bug, so in that case it will be + * easier for reproduce, we could at least log value returned from time(). + * + * TODO: To make it stable, we also need to make stable redisGetAsicView + * since now order of items is random. Also redis result needs to be + * sorted. + */ + + // Read current and temporary views from REDIS. + + auto currentMap = m_client->getAsicView(); + auto temporaryMap = m_client->getTempAsicView(); + + if (currentMap.size() != temporaryMap.size()) + { + SWSS_LOG_THROW("current view switches: %zu != temporary view switches: %zu, FATAL", + currentMap.size(), + temporaryMap.size()); + } + + if (currentMap.size() != m_switches.size()) + { + SWSS_LOG_THROW("current asic view switches %zu != defined switches %zu, FATAL", + currentMap.size(), + m_switches.size()); + } + + // VID of switches must match for each map + + for (auto& kvp: currentMap) + { + if (temporaryMap.find(kvp.first) == temporaryMap.end()) + { + SWSS_LOG_THROW("switch VID %s missing from temporary view!, FATAL", + sai_serialize_object_id(kvp.first).c_str()); + } + + if (m_switches.find(kvp.first) == m_switches.end()) + { + SWSS_LOG_THROW("switch VID %s missing from ASIC, FATAL", + sai_serialize_object_id(kvp.first).c_str()); + } + } + + std::vector> currentViews; + std::vector> tempViews; + std::vector> cls; + + try + { + for (auto& kvp: m_switches) + { + auto switchVid = kvp.first; + + auto sw = m_switches.at(switchVid); + + /* + * We are starting first stage here, it still can throw exceptions + * but it's non destructive for ASIC, so just catch and return in + * case of failure. + * + * Each ASIC view at this point will contain only 1 switch. + */ + + auto current = std::make_shared(currentMap.at(switchVid)); + auto temp = std::make_shared(temporaryMap.at(switchVid)); + + auto cl = std::make_shared(m_vendorSai, sw, m_handler, m_initViewRemovedVidSet, current, temp, m_breakConfig); + + cl->compareViews(); + + currentViews.push_back(current); + tempViews.push_back(temp); + cls.push_back(cl); + } + } + catch (const std::exception &e) + { + /* + * Exception was thrown in first stage, those were non destructive + * actions so just log exception and let syncd running. + */ + + SWSS_LOG_ERROR("Exception: %s", e.what()); + + return SAI_STATUS_FAILURE; + } + + /* + * This is second stage. Those operations are destructive, if any of them + * fail, then we will have inconsistent state in ASIC. + */ + + if (m_commandLineOptions->m_enableUnittests) + { + dumpComparisonLogicOutput(currentViews); + } + + for (auto& cl: cls) + { + cl->executeOperationsOnAsic(); // can throw, if so asic will be in inconsistent state + } + + updateRedisDatabase(tempViews); + + for (auto& cl: cls) + { + if (m_commandLineOptions->m_enableConsistencyCheck) + { + bool consistent = cl->checkAsicVsDatabaseConsistency(m_translator); + + if (!consistent && m_commandLineOptions->m_enableUnittests) + { + SWSS_LOG_THROW("ASIC content is different than DB content!"); + } + } + } + + return SAI_STATUS_SUCCESS; +} + +void Syncd::dumpComparisonLogicOutput( + _In_ const std::vector>& currentViews) +{ + SWSS_LOG_ENTER(); + + std::stringstream ss; + + size_t total = 0; // total operations from all switches + + for (auto& c: currentViews) + { + total += c->asicGetOperationsCount(); + } + + ss << "ASIC_OPERATIONS: " << total << std::endl; + + for (auto& c: currentViews) + { + ss << "ASIC_OPERATIONS on " + << sai_serialize_object_id(c->getSwitchVid()) + << " : " + << c->asicGetOperationsCount() + << std::endl; + + for (const auto &op: c->asicGetWithOptimizedRemoveOperations()) + { + const std::string &key = kfvKey(*op.m_op); + const std::string &opp = kfvOp(*op.m_op); + + ss << "o " << opp << ": " << key << std::endl; + + const auto &values = kfvFieldsValues(*op.m_op); + + for (auto v: values) + ss << "a: " << fvField(v) << " " << fvValue(v) << std::endl; + } + } + + std::ofstream log("applyview.log"); + + if (log.is_open()) + { + log << ss.str(); + + log.close(); + + SWSS_LOG_NOTICE("wrote apply_view asic operations to applyview.log"); + } + else + { + SWSS_LOG_ERROR("failed to open applyview.log"); + } +} + +void Syncd::updateRedisDatabase( + _In_ const std::vector>& temporaryViews) +{ + SWSS_LOG_ENTER(); + + // TODO: We can make LUA script for this which will be much faster. + // + // TODO: Needs to be revisited if ASIC views will be across multiple redis + // database indexes. + + SWSS_LOG_TIMER("redis update"); + + m_client->removeAsicStateTable(); + + m_client->removeTempAsicStateTable(); + + // Save temporary views as current view in redis database. + + for (auto& tv: temporaryViews) + { + for (const auto &pair: tv->m_soAll) + { + const auto &obj = pair.second; + + const auto &attr = obj->getAllAttributes(); + + std::vector entry; + + for (const auto &ap: attr) + { + const auto saiAttr = ap.second; + + entry.emplace_back(saiAttr->getStrAttrId(), saiAttr->getStrAttrValue()); + } + + m_client->createAsicObject(obj->m_meta_key, entry); + } + } + + /* + * Remove previous RID2VID maps and apply new map. + * + * NOTE: This needs to be done per switch, we can't remove all maps. + */ + + // TODO check if those 2 maps are consistent + + std::unordered_map allVid2Rid; + + for (auto& tv: temporaryViews) + { + for (auto &kv: tv->m_ridToVid) + { + allVid2Rid[kv.second] = kv.first; + } + } + + m_client->setVidAndRidMap(allVid2Rid); + + SWSS_LOG_NOTICE("updated redis database"); +} + +// TODO for future we can have each switch in separate redis db index or even +// some switches in the same db index and some in separate. Current redis get +// asic view is assuming all switches are in the same db index an also some +// operations per switch are accessing data base in SaiSwitch class. This +// needs to be reorganised to access database per switch basis and get only +// data that corresponds to each particular switch and access correct db index. + +void Syncd::onSyncdStart( + _In_ bool warmStart) +{ + SWSS_LOG_ENTER(); + + std::lock_guard lock(m_mutex); + + /* + * It may happen that after initialize we will receive some port + * notifications with port'ids that are not in redis db yet, so after + * checking VIDTORID map there will be entries and translate_vid_to_rid + * will generate new id's for ports, this may cause race condition so we + * need to use a lock here to prevent that. + */ + + SWSS_LOG_TIMER("on syncd start"); + + if (warmStart) + { + /* + * Switch was warm started, so switches map is empty, we need to + * recreate it based on existing entries inside database. + * + * Currently we expect only one switch, then we need to call it. + * + * Also this will make sure that current switch id is the same as + * before restart. + * + * If we want to support multiple switches, this needs to be adjusted. + */ + + performWarmRestart(); + + SWSS_LOG_NOTICE("skipping hard reinit since WARM start was performed"); + return; + } + + SWSS_LOG_NOTICE("performing hard reinit since COLD start was performed"); + + /* + * Switch was restarted in hard way, we need to perform hard reinit and + * recreate switches map. + */ + + if (m_switches.size()) + { + SWSS_LOG_THROW("performing hard reinit, but there are %zu switches defined, bug!", m_switches.size()); + } + + HardReiniter hr(m_client, m_translator, m_vendorSai, m_handler); + + m_switches = hr.hardReinit(); + + for (auto& sw: m_switches) + { + startDiagShell(sw.second->getRid()); + } + + SWSS_LOG_NOTICE("hard reinit succeeded"); +} + +void Syncd::onSwitchCreateInInitViewMode( + _In_ sai_object_id_t switchVid, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + /* + * We can have multiple switches here, but each switch is identified by + * SAI_SWITCH_ATTR_SWITCH_HARDWARE_INFO. This attribute is treated as key, + * so each switch will have different hardware info. + * + * Currently we assume that we have only one switch. + * + * We can have 2 scenarios here: + * + * - we have multiple switches already existing, and in init view mode user + * will create the same switches, then since switch id are deterministic + * we can match them by hardware info and by switch id, it may happen + * that switch id will be different if user will create switches in + * different order, this case will be not supported unless special logic + * will be written to handle that case. This case is solved by bounding + * hardware info to switch index in context config file. + * + * - if user created switches but non of switch has the same hardware info + * then it means we need to create actual switch here, since user will + * want to query switch ports etc values, that's why on create switch is + * special case, and that's why we need to keep track of all switches. + * This case is also solved bu allowing creation of only switches defined + * in context config which bounds hardware info and switch index making + * switch VID deterministic. + * + * Since we are creating switch here, we are sure that this switch don't + * have any oid attributes set, so we can pass all attributes. + * + * Hardware info attribute must be passed and all non OID attributes + * including create only and conditionals. + */ + + /* + * Multiple switches scenario with changed order: + * + * If orchagent will create the same switch with the same hardware info but + * with different order since switch id is deterministic, then VID of both + * switches will always match since they are bound to hardware info using + * context config file. + */ + + if (m_switches.find(switchVid) == m_switches.end()) + { + /* + * Switch with particular VID don't exists yet, so lets create it. We + * need to create this switch so user in init mode could query switch + * properties using GET api. + * + * We assume that none of attributes is object id attribute. + * + * This scenario can happen when you start syncd on empty database and + * then you quit and restart it again. + */ + + sai_object_id_t switchRid; + + sai_status_t status; + + { + SWSS_LOG_TIMER("cold boot: create switch"); + + status = m_vendorSai->create(SAI_OBJECT_TYPE_SWITCH, &switchRid, 0, attr_count, attr_list); + } + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_THROW("failed to create switch in init view mode: %s", + sai_serialize_status(status).c_str()); + } + + /* + * Object was created so new RID was generated we need to save virtual + * id's to redis db. + */ + + SWSS_LOG_NOTICE("created switch VID %s to RID %s in init view mode", + sai_serialize_object_id(switchVid).c_str(), + sai_serialize_object_id(switchRid).c_str()); + + m_translator->insertRidAndVid(switchRid, switchVid); + + // make switch initialization and get all default data + + m_switches[switchVid] = std::make_shared(switchVid, switchRid, m_client, m_translator, m_vendorSai, false); + + m_mdioIpcServer->setSwitchId(switchRid); + + startDiagShell(switchRid); + } + else + { + /* + * There is already switch defined, we need to match it by hardware + * info and we need to know that current switch VID also should match + * since it's deterministic created. + */ + + auto sw = m_switches.at(switchVid); + + // switches VID must match, since it's deterministic + + if (switchVid != sw->getVid()) + { + SWSS_LOG_THROW("created switch VID don't match: previous %s, current: %s", + sai_serialize_object_id(switchVid).c_str(), + sai_serialize_object_id(sw->getVid()).c_str()); + } + + // also hardware info also must match + + std::string currentHw = sw->getHardwareInfo(); + std::string newHw; + + auto attr = sai_metadata_get_attr_by_id(SAI_SWITCH_ATTR_SWITCH_HARDWARE_INFO, attr_count, attr_list); + + if (attr == NULL) + { + // this is ok, attribute doesn't exist, so assumption is empty string + } + else + { + newHw = std::string((char*)attr->value.s8list.list, attr->value.s8list.count); + } + + SWSS_LOG_NOTICE("new switch %s contains hardware info: '%s'", + sai_serialize_object_id(switchVid).c_str(), + newHw.c_str()); + + /* + * The line below is added due to a behavior change of SAI call. + * + * TODO: remove the line when SAI vendor agrees fix on their end. + */ + currentHw = currentHw == "none"? "" : currentHw; + + if (currentHw != newHw) + { + SWSS_LOG_THROW("hardware info mismatch: current '%s' vs new '%s'", currentHw.c_str(), newHw.c_str()); + } + + SWSS_LOG_NOTICE("current %s switch hardware info: '%s'", + sai_serialize_object_id(switchVid).c_str(), + currentHw.c_str()); + + /* + * Some attributes on new switch could be different then on existing + * one, but we are in init view mode so comparison logic will be + * executed on apply view and those attributes will be compared and + * actions will be generated if any of them are different. + */ + } +} + +void Syncd::performWarmRestartSingleSwitch( + _In_ const std::string& key) +{ + SWSS_LOG_ENTER(); + + // key should be in format ASIC_STATE:SAI_OBJECT_TYPE_SWITCH:oid:0xYYYY + + /* + * Since multiple switches can be defined on warm boot, then we need to + * correctly identify each switch by passing hardware info. + * + * TODO: do we also need to pass any other attributes, like create only etc? + */ + + auto start = key.find_first_of(":") + 1; + auto end = key.find(":", start); + + std::string strSwitchVid = key.substr(end + 1); + + std::vector values; + + auto hash = m_client->getAttributesFromAsicKey(key); + + SWSS_LOG_NOTICE("switch %s", strSwitchVid.c_str()); + + for (auto &kv: hash) + { + const std::string& skey = kv.first; + const std::string& svalue = kv.second; + + if (skey == "NULL") + continue; + + SWSS_LOG_NOTICE(" - attr: %s:%s", skey.c_str(), svalue.c_str()); + + swss::FieldValueTuple fvt(skey, svalue); + + values.push_back(fvt); + } + + SaiAttributeList list(SAI_OBJECT_TYPE_SWITCH, values, false); + + sai_object_id_t switchVid; + + sai_deserialize_object_id(strSwitchVid, switchVid); + + sai_object_id_t originalSwitchRid = m_translator->translateVidToRid(switchVid); + + sai_object_id_t switchRid; + + std::vector attrs; + + sai_attribute_t attr; + + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = true; + + attrs.push_back(attr); + + sai_attribute_t *attrList = list.get_attr_list(); + + uint32_t attrCount = list.get_attr_count(); + + for (uint32_t idx = 0; idx < attrCount; idx++) + { + auto id = attrList[idx].id; + + if (id == SAI_SWITCH_ATTR_INIT_SWITCH) + continue; + + auto meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_SWITCH, id); + + /* + * If we want to handle multiple switches, then during warm boot switch + * create we need to pass hardware info so vendor sai could know which + * switch to initialize. We also need to update pointer values since + * new process could be loaded at different address space. + */ + + if (id == SAI_SWITCH_ATTR_SWITCH_HARDWARE_INFO || meta->attrvaluetype == SAI_ATTR_VALUE_TYPE_POINTER) + { + attrs.push_back(attrList[idx]); + continue; + } + + SWSS_LOG_NOTICE("skipping warm boot: %s", meta->attridname); + } + + // TODO support multiple notification handlers + m_handler->updateNotificationsPointers((uint32_t)attrs.size(), attrs.data()); + + sai_status_t status; + + { + SWSS_LOG_TIMER("Warm boot: create switch VID: %s", sai_serialize_object_id(switchVid).c_str()); + + status = m_vendorSai->create(SAI_OBJECT_TYPE_SWITCH, &switchRid, 0, (uint32_t)attrs.size(), attrs.data()); + } + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_THROW("failed to create switch RID: %s for VID %s", + sai_serialize_status(status).c_str(), + sai_serialize_object_id(switchVid).c_str()); + } + + if (originalSwitchRid != switchRid) + { + SWSS_LOG_THROW("Unexpected RID 0x%" PRIx64 " (expected 0x%" PRIx64 " )", + switchRid, originalSwitchRid); + } + + // perform all get operations on existing switch + + auto sw = m_switches[switchVid] = std::make_shared(switchVid, switchRid, m_client, m_translator, m_vendorSai, true); + + startDiagShell(switchRid); +} + +void Syncd::performWarmRestart() +{ + SWSS_LOG_ENTER(); + + /* + * There should be no case when we are doing warm restart and there is no + * switch defined, we will throw at such a case. + * + * This case could be possible when no switches were created and only api + * was initialized, but we will skip this scenario and address is when we + * will have need for it. + */ + + auto entries = m_client->getAsicStateSwitchesKeys(); + + if (entries.size() == 0) + { + SWSS_LOG_THROW("on warm restart there is no switches defined in DB, not supported yet, FIXME"); + } + + SWSS_LOG_NOTICE("switches defined in warm restart: %zu", entries.size()); + + // here we could have multiple switches defined, let's process them one by one + + for (auto& entry: entries) + { + performWarmRestartSingleSwitch(entry); + } +} + +void Syncd::startDiagShell( + _In_ sai_object_id_t switchRid) +{ + SWSS_LOG_ENTER(); + + if (m_commandLineOptions->m_enableDiagShell) + { + SWSS_LOG_NOTICE("starting diag shell thread for switch RID %s", + sai_serialize_object_id(switchRid).c_str()); + + std::thread thread = std::thread(&Syncd::diagShellThreadProc, this, switchRid); + + thread.detach(); + } +} + +void Syncd::diagShellThreadProc( + _In_ sai_object_id_t switchRid) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + + /* + * This is currently blocking API on broadcom, it will block until we exit + * shell. + */ + + while (true) + { + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_SWITCH_SHELL_ENABLE; + attr.value.booldata = true; + + status = m_vendorSai->set(SAI_OBJECT_TYPE_SWITCH, switchRid, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to enable switch shell: %s", + sai_serialize_status(status).c_str()); + return; + } + + sleep(1); + } +} + +void Syncd::sendShutdownRequest( + _In_ sai_object_id_t switchVid) +{ + SWSS_LOG_ENTER(); + + if (m_notifications == nullptr) + { + SWSS_LOG_WARN("notifications pointer is NULL"); + return; + } + + auto s = sai_serialize_object_id(switchVid); + + SWSS_LOG_NOTICE("sending switch_shutdown_request notification to OA for switch: %s", s.c_str()); + + std::vector entry; + + // TODO use m_handler->onSwitchShutdownRequest(switchVid); (but this should be per switch) + + s = sai_serialize_switch_shutdown_request(switchVid); + + m_notifications->send(SAI_SWITCH_NOTIFICATION_NAME_SWITCH_SHUTDOWN_REQUEST, s, entry); +} + +void Syncd::sendShutdownRequestAfterException() +{ + SWSS_LOG_ENTER(); + + std::lock_guard lock(m_mutex); + + try + { + if (m_switches.size()) + { + for (auto& kvp: m_switches) + { + sendShutdownRequest(kvp.second->getVid()); + } + } + else + { + sendShutdownRequest(SAI_NULL_OBJECT_ID); + } + + SWSS_LOG_NOTICE("notification send successfully"); + } + catch(const std::exception &e) + { + SWSS_LOG_ERROR("Runtime error: %s", e.what()); + } + catch(...) + { + SWSS_LOG_ERROR("Unknown runtime error"); + } +} + +void Syncd::saiLoglevelNotify( + _In_ std::string strApi, + _In_ std::string strLogLevel) +{ + SWSS_LOG_ENTER(); + + try + { + sai_log_level_t logLevel; + sai_deserialize_log_level(strLogLevel, logLevel); + + sai_api_t api; + sai_deserialize_api(strApi, api); + + sai_status_t status = m_vendorSai->logSet(api, logLevel); + + if (status == SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("Setting SAI loglevel %s on %s", strLogLevel.c_str(), strApi.c_str()); + } + else + { + SWSS_LOG_INFO("set loglevel failed: %s", sai_serialize_status(status).c_str()); + } + } + catch (const std::exception& e) + { + SWSS_LOG_ERROR("Failed to set loglevel to %s on %s: %s", + strLogLevel.c_str(), + strApi.c_str(), + e.what()); + } +} + +void Syncd::setSaiApiLogLevel() +{ + SWSS_LOG_ENTER(); + + // We start from 1 since 0 is SAI_API_UNSPECIFIED. + + for (uint32_t idx = 1; idx < sai_metadata_enum_sai_api_t.valuescount; ++idx) + { + // NOTE: link to db is singleton, so if we would want multiple Syncd + // instances running at the same process, we need to have logger + // registrar similar to net link messages + + swss::Logger::linkToDb( + sai_metadata_enum_sai_api_t.valuesnames[idx], + std::bind(&Syncd::saiLoglevelNotify, this, _1, _2), + sai_serialize_log_level(SAI_LOG_LEVEL_NOTICE)); + } +} + +sai_status_t Syncd::removeAllSwitches() +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("Removing all switches"); + + // TODO mutex ? + + sai_status_t result = SAI_STATUS_SUCCESS; + + for (auto& sw: m_switches) + { + auto rid = sw.second->getRid(); + + auto strRid = sai_serialize_object_id(rid); + + SWSS_LOG_TIMER("removing switch RID %s", strRid.c_str()); + + auto status = m_vendorSai->remove(SAI_OBJECT_TYPE_SWITCH, rid); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("Can't delete a switch RID %s: %s", + strRid.c_str(), + sai_serialize_status(status).c_str()); + + result = status; + } + } + + return result; +} + +sai_status_t Syncd::setRestartWarmOnAllSwitches( + _In_ bool flag) +{ + SWSS_LOG_ENTER(); + + sai_status_t result = SAI_STATUS_SUCCESS; + + sai_attribute_t attr; + + attr.id = SAI_SWITCH_ATTR_RESTART_WARM; + attr.value.booldata = flag; + + for (auto& sw: m_switches) + { + auto rid = sw.second->getRid(); + + auto strRid = sai_serialize_object_id(rid); + + auto status = m_vendorSai->set(SAI_OBJECT_TYPE_SWITCH, rid, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_RESTART_WARM=%s: %s:%s", + (flag ? "true" : "false"), + strRid.c_str(), + sai_serialize_status(status).c_str()); + + result = status; + } + } + + return result; +} + +sai_status_t Syncd::setFastAPIEnableOnAllSwitches() +{ + SWSS_LOG_ENTER(); + + sai_status_t result = SAI_STATUS_SUCCESS; + + sai_attribute_t attr; + + attr.id = SAI_SWITCH_ATTR_FAST_API_ENABLE; + attr.value.booldata = true; + + for (auto& sw: m_switches) + { + auto rid = sw.second->getRid(); + + auto strRid = sai_serialize_object_id(rid); + + auto status = m_vendorSai->set(SAI_OBJECT_TYPE_SWITCH, rid, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_PRE_SHUTDOWN=true: %s:%s", + strRid.c_str(), + sai_serialize_status(status).c_str()); + + result = status; + break; + } + } + + return result; +} + +sai_status_t Syncd::setPreShutdownOnAllSwitches() +{ + SWSS_LOG_ENTER(); + + sai_status_t result = SAI_STATUS_SUCCESS; + + sai_attribute_t attr; + + attr.id = SAI_SWITCH_ATTR_PRE_SHUTDOWN; + attr.value.booldata = true; + + for (auto& sw: m_switches) + { + auto rid = sw.second->getRid(); + + auto strRid = sai_serialize_object_id(rid); + + auto status = m_vendorSai->set(SAI_OBJECT_TYPE_SWITCH, rid, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_PRE_SHUTDOWN=true: %s:%s", + strRid.c_str(), + sai_serialize_status(status).c_str()); + + result = status; + } + } + + return result; +} + +sai_status_t Syncd::setUninitDataPlaneOnRemovalOnAllSwitches() +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("Fast/warm reboot requested, keeping data plane running"); + + sai_status_t result = SAI_STATUS_SUCCESS; + + sai_attribute_t attr; + + attr.id = SAI_SWITCH_ATTR_UNINIT_DATA_PLANE_ON_REMOVAL; + attr.value.booldata = false; + + for (auto& sw: m_switches) + { + auto rid = sw.second->getRid(); + + auto strRid = sai_serialize_object_id(rid); + + sai_attr_capability_t attr_capability = {}; + + sai_status_t queryStatus; + + queryStatus = m_vendorSai->queryAttributeCapability(rid, + SAI_OBJECT_TYPE_SWITCH, + SAI_SWITCH_ATTR_UNINIT_DATA_PLANE_ON_REMOVAL, + &attr_capability); + if (queryStatus != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get SAI_SWITCH_ATTR_UNINIT_DATA_PLANE_ON_REMOVAL capabilities: %s:%s", + strRid.c_str(), + sai_serialize_status(queryStatus).c_str()); + + result = queryStatus; + continue; + } + + if (attr_capability.set_implemented) + { + auto status = m_vendorSai->set(SAI_OBJECT_TYPE_SWITCH, rid, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_UNINIT_DATA_PLANE_ON_REMOVAL=false: %s:%s", + strRid.c_str(), + sai_serialize_status(status).c_str()); + + result = status; + } + } + } + + return result; +} + +void Syncd::syncProcessNotification( + _In_ const swss::KeyOpFieldsValuesTuple& item) +{ + std::lock_guard lock(m_mutex); + + SWSS_LOG_ENTER(); + + m_processor->syncProcessNotification(item); +} + +bool Syncd::isVeryFirstRun() +{ + SWSS_LOG_ENTER(); + + /* + * If lane map is not defined in redis db then we assume this is very first + * start of syncd later on we can add additional checks here. + * + * TODO: if we add more switches then we need lane maps per switch. + * TODO: we also need other way to check if this is first start + * + * We could use VIDCOUNTER also, but if something is defined in the DB then + * we assume this is not the first start. + * + * TODO we need to fix this, since when there will be queue, it will still think + * this is first run, let's query HIDDEN ? + */ + + bool firstRun = m_client->hasNoHiddenKeysDefined(); + + SWSS_LOG_NOTICE("First Run: %s", firstRun ? "True" : "False"); + + return firstRun; +} + +static void timerWatchdogCallback( + _In_ int64_t span) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_ERROR("main loop execution exceeded %ld ms", span/1000); +} + +void Syncd::run() +{ + SWSS_LOG_ENTER(); + + WarmRestartTable warmRestartTable("STATE_DB"); // TODO from config + + syncd_restart_type_t shutdownType = SYNCD_RESTART_TYPE_COLD; + + volatile bool runMainLoop = true; + + bool inShutdownWaitMode = false; + + std::shared_ptr s = std::make_shared(); + + try + { + onSyncdStart(m_commandLineOptions->m_startType == SAI_START_TYPE_WARM_BOOT); + + // create notifications processing thread after we create_switch to + // make sure, we have switch_id translated to VID before we start + // processing possible quick fdb notifications, and pointer for + // notification queue is created before we create switch + m_processor->startNotificationsProcessingThread(); + + for (auto& sw: m_switches) + { + m_mdioIpcServer->setSwitchId(sw.second->getRid()); + } + + m_mdioIpcServer->startMdioThread(); + + SWSS_LOG_NOTICE("syncd listening for events"); + + s->addSelectable(m_selectableChannel.get()); + s->addSelectable(m_restartQuery.get()); + s->addSelectable(m_flexCounter.get()); + s->addSelectable(m_flexCounterGroup.get()); + + SWSS_LOG_NOTICE("starting main loop"); + } + catch(const std::exception &e) + { + SWSS_LOG_ERROR("Runtime error during syncd init: %s", e.what()); + + sendShutdownRequestAfterException(); + + s = std::make_shared(); + + s->addSelectable(m_restartQuery.get()); + s->addSelectable(m_selectableChannel.get()); + + inShutdownWaitMode = true; + + SWSS_LOG_NOTICE("starting main loop, ONLY restart query"); + + if (m_commandLineOptions->m_disableExitSleep) + runMainLoop = false; + } + + m_timerWatchdog.setCallback(timerWatchdogCallback); + + while (runMainLoop) + { + try + { + swss::Selectable *sel = NULL; + + int result = s->select(&sel); + + if (sel == m_restartQuery.get()) + { + /* + * This is actual a bad design, since selectable may pick up + * multiple events from the queue, and after restart those + * events will be forgotten since they were consumed already and + * this may lead to forget populate object table which will + * lead to unable to find some objects. + */ + + SWSS_LOG_NOTICE("is asic queue empty: %d", m_selectableChannel->empty()); + + while (!m_selectableChannel->empty()) + { + processEvent(*m_selectableChannel.get()); + } + + SWSS_LOG_NOTICE("drained queue"); + + WatchdogScope ws(m_timerWatchdog, "restart query"); + + shutdownType = handleRestartQuery(*m_restartQuery); + + if (shutdownType != SYNCD_RESTART_TYPE_PRE_SHUTDOWN && shutdownType != SYNCD_RESTART_TYPE_PRE_EXPRESS_SHUTDOWN) + { + // break out the event handling loop to shutdown syncd + runMainLoop = false; + break; + } + + // Handle switch pre-shutdown and wait for the final shutdown + // event + + SWSS_LOG_TIMER("%s pre-shutdown", (shutdownType == SYNCD_RESTART_TYPE_PRE_SHUTDOWN) ? "warm" : "express"); + + m_manager->removeAllCounters(); + + sai_status_t status = setRestartWarmOnAllSwitches(true); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_RESTART_WARM=true: %s for pre-shutdown", + sai_serialize_status(status).c_str()); + + shutdownType = SYNCD_RESTART_TYPE_COLD; + + warmRestartTable.setFlagFailed(); + continue; + } + + if (shutdownType == SYNCD_RESTART_TYPE_PRE_EXPRESS_SHUTDOWN) + { + SWSS_LOG_NOTICE("express boot, enable fast API pre-shutdown"); + status = setFastAPIEnableOnAllSwitches(); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_FAST_API_ENABLE=true: %s for express pre-shutdown. Fall back to cold restart", + sai_serialize_status(status).c_str()); + + shutdownType = SYNCD_RESTART_TYPE_COLD; + + warmRestartTable.setFlagFailed(); + continue; + } + } + + status = setPreShutdownOnAllSwitches(); + + if (status == SAI_STATUS_SUCCESS) + { + warmRestartTable.setPreShutdown(true); + + s = std::make_shared(); // make sure previous select is destroyed + + s->addSelectable(m_restartQuery.get()); + + SWSS_LOG_NOTICE("switched to PRE_SHUTDOWN, from now on accepting only shutdown requests"); + } + else + { + SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_PRE_SHUTDOWN=true: %s", + sai_serialize_status(status).c_str()); + + warmRestartTable.setPreShutdown(false); + + // Restore cold shutdown. + + setRestartWarmOnAllSwitches(false); + } + } + else if (sel == m_flexCounter.get()) + { + processFlexCounterEvent(*(swss::ConsumerTable*)sel); + } + else if (sel == m_flexCounterGroup.get()) + { + processFlexCounterGroupEvent(*(swss::ConsumerTable*)sel); + } + else if (sel == m_selectableChannel.get()) + { + if (inShutdownWaitMode) + { + processEventInShutdownWaitMode(*m_selectableChannel.get()); + } + else + { + processEvent(*m_selectableChannel.get()); + } + } + else + { + SWSS_LOG_ERROR("select failed: %d", result); + } + } + catch(const std::exception &e) + { + SWSS_LOG_ERROR("Runtime error: %s - entering shutdown-wait mode", e.what()); + + sendShutdownRequestAfterException(); + + s = std::make_shared(); + + s->addSelectable(m_restartQuery.get()); + s->addSelectable(m_selectableChannel.get()); + + inShutdownWaitMode = true; + + if (m_commandLineOptions->m_disableExitSleep) + runMainLoop = false; + + // make sure that if second exception will arise, then we break the loop + m_commandLineOptions->m_disableExitSleep = true; + } + } + + WatchdogScope ws(m_timerWatchdog, "shutting down syncd"); + + if (shutdownType == SYNCD_RESTART_TYPE_WARM) + { + const char *warmBootWriteFile = profileGetValue(0, SAI_KEY_WARM_BOOT_WRITE_FILE); + + SWSS_LOG_NOTICE("using warmBootWriteFile: '%s'", warmBootWriteFile); + + if (warmBootWriteFile == NULL) + { + SWSS_LOG_WARN("user requested warm shutdown but warmBootWriteFile is not specified, forcing cold shutdown"); + + shutdownType = SYNCD_RESTART_TYPE_COLD; + warmRestartTable.setWarmShutdown(false); + } + else + { + SWSS_LOG_NOTICE("Warm Reboot requested, keeping data plane running"); + + sai_status_t status = setRestartWarmOnAllSwitches(true); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_RESTART_WARM=true: %s, fall back to cold restart", + sai_serialize_status(status).c_str()); + + shutdownType = SYNCD_RESTART_TYPE_COLD; + + warmRestartTable.setFlagFailed(); + } + } + } + + if (shutdownType == SYNCD_RESTART_TYPE_FAST || shutdownType == SYNCD_RESTART_TYPE_WARM || shutdownType == SYNCD_RESTART_TYPE_EXPRESS) + { + setUninitDataPlaneOnRemovalOnAllSwitches(); + } + + m_manager->removeAllCounters(); + + m_mdioIpcServer->stopMdioThread(); + + sai_status_t status = removeAllSwitches(); + + // Stop notification thread after removing switch + m_processor->stopNotificationsProcessingThread(); + + if (shutdownType == SYNCD_RESTART_TYPE_WARM || shutdownType == SYNCD_RESTART_TYPE_EXPRESS) + { + warmRestartTable.setWarmShutdown(status == SAI_STATUS_SUCCESS); + } + + SWSS_LOG_NOTICE("calling api uninitialize"); + + status = m_vendorSai->apiUninitialize(); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("failed to uninitialize api: %s", sai_serialize_status(status).c_str()); + } + + SWSS_LOG_NOTICE("uninitialize finished"); +} + +syncd_restart_type_t Syncd::handleRestartQuery( + _In_ swss::NotificationConsumer &restartQuery) +{ + SWSS_LOG_ENTER(); + + std::string op; + std::string data; + std::vector values; + + restartQuery.pop(op, data, values); + + m_timerWatchdog.setEventData(op + ":" + data); + + SWSS_LOG_NOTICE("received %s switch shutdown event", op.c_str()); + + return RequestShutdownCommandLineOptions::stringToRestartType(op); +} diff --git a/syncd/Syncd.h b/syncd/Syncd.h index d633c9196d..e378919ef9 100644 --- a/syncd/Syncd.h +++ b/syncd/Syncd.h @@ -27,9 +27,67 @@ #include "swss/notificationconsumer.h" #include +#include +#include namespace syncd { + /** + * @brief Link event damping configuration and state per port + */ + struct LinkEventDampingPortState + { + // Configuration parameters + sai_redis_link_event_damping_algorithm_t algorithm; + sai_redis_link_event_damping_algo_aied_config_t aied_config; + + // Runtime state for AIED algorithm + uint32_t current_penalty; // Current penalty value + uint64_t last_transition_time_ms; // Timestamp of last transition (milliseconds) + uint64_t last_decay_time_ms; // Timestamp of last decay calculation (milliseconds) + uint64_t damping_start_time_ms; // When damping state started (milliseconds) + bool is_damping_active; // Whether link is currently in damped state + sai_port_oper_status_t physical_status; // Physical port status + sai_port_oper_status_t advertised_status; // Last advertised status (may differ due to damping) + sai_port_oper_status_t last_suppressed_status; // Last event suppressed while damping + bool pending_state_sync; // Flag to indicate state mismatch needs propagation + + // Counters for observability + uint64_t pre_damping_link_transitions; + uint64_t pre_damping_up_events; + uint64_t pre_damping_down_events; + uint64_t post_damping_up_events; + uint64_t post_damping_down_events; + uint64_t post_damping_link_transitions; + + // Constructor with defaults + LinkEventDampingPortState() + : algorithm(SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED), + current_penalty(0), + last_transition_time_ms(0), + last_decay_time_ms(0), + damping_start_time_ms(0), + is_damping_active(false), + physical_status(SAI_PORT_OPER_STATUS_UNKNOWN), + advertised_status(SAI_PORT_OPER_STATUS_UNKNOWN), + last_suppressed_status(SAI_PORT_OPER_STATUS_UNKNOWN), + pending_state_sync(false), + pre_damping_link_transitions(0), + pre_damping_up_events(0), + pre_damping_down_events(0), + post_damping_up_events(0), + post_damping_down_events(0), + post_damping_link_transitions(0) + { + // Initialize AIED config with defaults + aied_config.max_suppress_time = 0; + aied_config.suppress_threshold = 0; + aied_config.reuse_threshold = 0; + aied_config.decay_half_life = 0; + aied_config.flap_penalty = 0; + } + }; + class Syncd { private: @@ -219,6 +277,81 @@ namespace syncd _In_ const std::vector &values, _In_ bool fromAsicChannel=true); + sai_status_t processLinkEventDampingConfigSet( + _In_ const swss::KeyOpFieldsValuesTuple &kco); + + private: // link event damping helpers + + /** + * @brief Apply link event damping algorithm to a port state change + * @param portVid Virtual object ID of the port + * @param newStatus New operational status of the port + * @return true if notification should be suppressed, false if it should be propagated + */ + bool applyLinkEventDamping( + _In_ sai_object_id_t portVid, + _In_ sai_port_oper_status_t newStatus); + + /** + * @brief Apply AIED damping algorithm + * @param state Port damping state + * @param newStatus New operational status + * @param currentTimeMs Current time in milliseconds + * @return true if should suppress, false if should propagate + */ + bool applyAiedAlgorithm( + _In_ sai_object_id_t portVid, + _In_ LinkEventDampingPortState& state, + _In_ sai_port_oper_status_t newStatus, + _In_ uint64_t currentTimeMs); + + /** + * @brief Decay penalty based on time elapsed + * @param state Port damping state + * @param currentTimeMs Current time in milliseconds + */ + void decayPenalty( + _In_ LinkEventDampingPortState& state, + _In_ uint64_t currentTimeMs); + + /** + * @brief Get current time in milliseconds + * @return Current time in milliseconds since epoch + */ + uint64_t getCurrentTimeMs(); + + /** + * @brief Proactively check all damped ports and enforce max_suppress_time + * Called periodically by timer thread to ensure ports don't exceed max_suppress_time + * even when no new port events arrive + */ + void checkDampedPortsTimeout(); + + /** + * @brief Timer thread function for proactive damping timeout enforcement + * Runs periodically to check if any damped ports have exceeded max_suppress_time + */ + void dampingTimerThreadFunc(); + + /** + * @brief Start the damping timer thread + */ + void startDampingTimerThread(); + + /** + * @brief Stop the damping timer thread + */ + void stopDampingTimerThread(); + + /** + * @brief Write damping counters to STATE_DB for a specific port + * @param portVid Virtual object ID of the port + * @param state Port damping state containing counters + */ + void writeDampingCountersToStateDb( + _In_ sai_object_id_t portVid, + _In_ const LinkEventDampingPortState& state); + private: // process quad oid sai_status_t processOidCreate( @@ -395,6 +528,9 @@ namespace syncd void sendNotifyResponse( _In_ sai_status_t status); + void sendLinkEventDampingConfigResponse( + _In_ sai_status_t status); + private: // snoop get response oids void snoopGetResponse( @@ -547,5 +683,46 @@ namespace syncd TimerWatchdog m_timerWatchdog; std::set m_createdInInitView; + + /** + * @brief Link event damping configuration per port + * Key: Port VID, Value: Damping state and configuration + */ + std::map m_portLinkEventDampingStates; + + /** + * @brief Mutex to protect link event damping state + */ + std::mutex m_linkEventDampingMutex; + + /** + * @brief STATE_DB connection for writing damping counters + */ + std::shared_ptr m_dbState; + + /** + * @brief STATE_DB table for damping counters + */ + std::shared_ptr m_dampingCounterTable; + + /** + * @brief Timer thread for proactive damping timeout enforcement + */ + std::shared_ptr m_dampingTimerThread; + + /** + * @brief Flag to control damping timer thread execution + */ + bool m_runDampingTimerThread; + + /** + * @brief Condition variable for damping timer thread + */ + std::condition_variable m_dampingTimerCv; + + /** + * @brief Mutex for damping timer thread synchronization + */ + std::mutex m_dampingTimerMutex; }; } diff --git a/syncd/tests/Makefile.am b/syncd/tests/Makefile.am index 2630eecdc9..f548d69c27 100644 --- a/syncd/tests/Makefile.am +++ b/syncd/tests/Makefile.am @@ -5,7 +5,7 @@ LDADD_GTEST = -L/usr/src/gtest -lgtest -lgtest_main bin_PROGRAMS = tests tests_SOURCES = \ - main.cpp TestSyncdBrcm.cpp TestSyncdMlnx.cpp TestSyncdNvdaBf.cpp TestSyncdLib.cpp TestDisabledRedisClient.cpp + main.cpp TestSyncdBrcm.cpp TestSyncdMlnx.cpp TestSyncdNvdaBf.cpp TestSyncdLib.cpp TestSyncdLinkEventDamping.cpp TestDisabledRedisClient.cpp tests_CXXFLAGS = \ $(DBGFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS_COMMON) tests_LDADD = \ diff --git a/syncd/tests/TestSyncdLinkEventDamping.cpp b/syncd/tests/TestSyncdLinkEventDamping.cpp new file mode 100644 index 0000000000..f955e82bf5 --- /dev/null +++ b/syncd/tests/TestSyncdLinkEventDamping.cpp @@ -0,0 +1,255 @@ +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include "swss/select.h" + +#include "Sai.h" +#include "Syncd.h" +#include "MetadataLogger.h" + +#include "TestSyncdLib.h" + +#include "meta/sai_serialize.h" +#include "sairediscommon.h" +#include "meta/RedisSelectableChannel.h" + +using namespace syncd; + +static const char* profile_get_value( + _In_ sai_switch_profile_id_t profile_id, + _In_ const char* variable) +{ + SWSS_LOG_ENTER(); + + return NULL; +} + +static int profile_get_next_value( + _In_ sai_switch_profile_id_t profile_id, + _Out_ const char** variable, + _Out_ const char** value) +{ + SWSS_LOG_ENTER(); + + if (value == NULL) + { + SWSS_LOG_INFO("resetting profile map iterator"); + return 0; + } + + if (variable == NULL) + { + SWSS_LOG_WARN("variable is null"); + return -1; + } + + SWSS_LOG_INFO("iterator reached end"); + return -1; +} + +static sai_service_method_table_t test_services = { + profile_get_value, + profile_get_next_value +}; + +void syncdLinkEventDampingWorkerThread() +{ + SWSS_LOG_ENTER(); + + swss::Logger::getInstance().setMinPrio(swss::Logger::SWSS_NOTICE); + MetadataLogger::initialize(); + + auto vendorSai = std::make_shared(); + auto commandLineOptions = std::make_shared(); + auto isWarmStart = false; + + commandLineOptions->m_enableSyncMode= true; + commandLineOptions->m_enableTempView = true; + commandLineOptions->m_disableExitSleep = true; + commandLineOptions->m_enableUnittests = true; + commandLineOptions->m_enableSaiBulkSupport = true; + commandLineOptions->m_startType = SAI_START_TYPE_COLD_BOOT; + commandLineOptions->m_redisCommunicationMode = SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC; + commandLineOptions->m_profileMapFile = "./brcm/testprofile.ini"; + + auto syncd = std::make_shared(vendorSai, commandLineOptions, isWarmStart); + syncd->run(); + + SWSS_LOG_NOTICE("Started syncd worker."); +} + +class LinkEventDampingTest : public ::testing::Test +{ +public: + LinkEventDampingTest() + { + SWSS_LOG_ENTER(); + + auto dbAsic = std::make_shared("ASIC_DB", 0); + + m_selectableChannel = std::make_shared( + dbAsic, + REDIS_TABLE_GETRESPONSE, + ASIC_STATE_TABLE, + TEMP_PREFIX, + false); + } + + virtual ~LinkEventDampingTest() = default; + +public: + virtual void SetUp() override + { + SWSS_LOG_ENTER(); + + m_switchId = SAI_NULL_OBJECT_ID; + + // flush ASIC DB + flushAsicDb(); + + syncdStart(); + createSwitch(); + } + + void syncdStart() + { + SWSS_LOG_ENTER(); + + // start syncd worker + m_worker = std::make_shared(syncdLinkEventDampingWorkerThread); + + // initialize SAI redis + m_sairedis = std::make_shared(); + + auto status = m_sairedis->apiInitialize(0, &test_services); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // set communication mode + sai_attribute_t attr; + + attr.id = SAI_REDIS_SWITCH_ATTR_REDIS_COMMUNICATION_MODE; + attr.value.s32 = SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC; + + status = m_sairedis->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // enable recording + attr.id = SAI_REDIS_SWITCH_ATTR_RECORD; + attr.value.booldata = true; + + status = m_sairedis->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + } + + void createSwitch() + { + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + + // init view + attr.id = SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD; + attr.value.s32 = SAI_REDIS_NOTIFY_SYNCD_INIT_VIEW; + + auto status = m_sairedis->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // apply view + attr.id = SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD; + attr.value.s32 = SAI_REDIS_NOTIFY_SYNCD_APPLY_VIEW; + + status = m_sairedis->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // create switch + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = true; + + status = m_sairedis->create(SAI_OBJECT_TYPE_SWITCH, &m_switchId, SAI_NULL_OBJECT_ID, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + } + + virtual void TearDown() override + { + SWSS_LOG_ENTER(); + + // uninitialize SAI redis + + auto status = m_sairedis->apiUninitialize(); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // stop syncd worker + sendSyncdShutdownNotification(); + m_worker->join(); + } + +protected: + std::shared_ptr m_worker; + std::shared_ptr m_sairedis; + sai_object_id_t m_switchId; + std::shared_ptr m_selectableChannel; +}; + +sai_status_t getResponseStatus( + _In_ const std::string& command, + _In_ sairedis::RedisSelectableChannel *selectable, + _In_ bool init_view_mode) +{ + SWSS_LOG_ENTER(); + + swss::Select s; + s.addSelectable(selectable); + + while (true) + { + swss::Selectable *sel; + int result = s.select(&sel, 1000); + + if (result == swss::Select::OBJECT) + { + swss::KeyOpFieldsValuesTuple kco; + selectable->pop(kco, init_view_mode); + + const std::string &op = kfvOp(kco); + const std::string &opkey = kfvKey(kco); + + if (op != command) + { + SWSS_LOG_WARN("got not expected response: %s:%s", opkey.c_str(), op.c_str()); + continue; + } + + sai_status_t status; + sai_deserialize_status(opkey, status); + + return status; + } + + SWSS_LOG_ERROR("SELECT operation result: %s on %s", swss::Select::resultToString(result).c_str(), command.c_str()); + break; + } + + return SAI_STATUS_FAILURE; +} + +TEST_F(LinkEventDampingTest, SetLinkEventDampingConfigNotImplemented) +{ + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + sai_serialize_object_id(SAI_NULL_OBJECT_ID); + + std::string str_attr_id = sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); + + std::string str_attr_value = sai_serialize_redis_link_event_damping_algorithm(SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + m_selectableChannel->set(key, {swss::FieldValueTuple(str_attr_id, str_attr_value)}, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, m_selectableChannel.get(), false), SAI_STATUS_NOT_IMPLEMENTED); +} diff --git a/unittest/lib/TestClientServerSai.cpp b/unittest/lib/TestClientServerSai.cpp index 3eb65733a6..27e24ebd97 100644 --- a/unittest/lib/TestClientServerSai.cpp +++ b/unittest/lib/TestClientServerSai.cpp @@ -146,6 +146,74 @@ TEST(ClientServerSai, logSet) EXPECT_EQ(SAI_STATUS_SUCCESS, css->logSet(SAI_API_PORT, SAI_LOG_LEVEL_NOTICE)); } +TEST(ClientServerSai, VerifySaiRedisPortAttrNotSupportedInClientMode) +{ + auto css = std::make_shared(); + + // Initialize as sairedis client. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_client_services)); + + sai_attribute_t attr; + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; + attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED; + + EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); +} + +TEST(ClientServerSai, SetLinkEventDampingAlgorithm) +{ + auto css = std::make_shared(); + + // Initialize as sairedis server. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); + + sai_attribute_t attr; + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; + attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED; + + EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); +} + +TEST(ClientServerSai, SetLinkEventDampingConfig) +{ + auto css = std::make_shared(); + + // Initialize as sairedis server. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); + + // Failure when config is NULL. + sai_attribute_t attr; + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG; + attr.value.ptr = nullptr; + + EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); + + sai_redis_link_event_damping_algo_aied_config_t config = { + .max_suppress_time = 5000, + .suppress_threshold = 1500, + .reuse_threshold = 1200, + .decay_half_life = 3000, + .flap_penalty = 1000}; + + attr.value.ptr = (void *) &config; + + EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); +} + +TEST(ClientServerSai, SetInvalidSaiRedisPortAttribute) +{ + auto css = std::make_shared(); + + // Initialize as sairedis server. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); + + sai_attribute_t attr; + // Set an id that is not supported yet. + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG + 100; + + EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); +} + TEST(ClientServerSai, bulkGetClearStats) { auto css = std::make_shared(); From 23b1b0f8f0708ef7363e31ed935e256ede34fee5 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Fri, 22 May 2026 15:07:54 +0530 Subject: [PATCH 02/35] Revert "Adding link event damping support" This reverts commit 0953e6858d279ebf8251b402679d96b5041d0546. Signed-off-by: Sivakumar Thirukkanna Thevar --- lib/ClientSai.cpp | 3 +- lib/RedisRemoteSaiInterface.cpp | 96 - lib/RedisRemoteSaiInterface.h | 19 - lib/Sai.cpp | 6 - lib/sairediscommon.h | 2 - syncd/NotificationProcessor.cpp | 46 +- syncd/NotificationProcessor.h | 9 +- syncd/Syncd.cpp | 826 +-- syncd/Syncd.cpp.orig | 6105 --------------------- syncd/Syncd.h | 177 - syncd/tests/Makefile.am | 2 +- syncd/tests/TestSyncdLinkEventDamping.cpp | 255 - unittest/lib/TestClientServerSai.cpp | 68 - 13 files changed, 12 insertions(+), 7602 deletions(-) delete mode 100644 syncd/Syncd.cpp.orig delete mode 100644 syncd/tests/TestSyncdLinkEventDamping.cpp diff --git a/lib/ClientSai.cpp b/lib/ClientSai.cpp index 06829109b8..63bc2392e2 100644 --- a/lib/ClientSai.cpp +++ b/lib/ClientSai.cpp @@ -240,8 +240,7 @@ sai_status_t ClientSai::set( SWSS_LOG_ENTER(); REDIS_CHECK_API_INITIALIZED(); - if (RedisRemoteSaiInterface::isRedisAttribute(objectType, attr) || - RedisRemoteSaiInterface::isRedisPortAttribute(objectType, attr)) + if (RedisRemoteSaiInterface::isRedisAttribute(objectType, attr)) { SWSS_LOG_ERROR("sairedis extension attributes are not supported in CLIENT mode"); diff --git a/lib/RedisRemoteSaiInterface.cpp b/lib/RedisRemoteSaiInterface.cpp index ada0201110..dabd7ad0b0 100644 --- a/lib/RedisRemoteSaiInterface.cpp +++ b/lib/RedisRemoteSaiInterface.cpp @@ -532,83 +532,6 @@ sai_status_t RedisRemoteSaiInterface::setRedisExtensionAttribute( return SAI_STATUS_FAILURE; } -sai_status_t RedisRemoteSaiInterface::setLinkEventDampingConfig( - _In_ sai_object_type_t objectType, - _In_ sai_object_id_t objectId, - _In_ const std::vector &values) -{ - SWSS_LOG_ENTER(); - - std::string key = sai_serialize_object_type(objectType) + ":" + sai_serialize_object_id(objectId); - - m_communicationChannel->set(key, values, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - - if (m_syncMode) - { - swss::KeyOpFieldsValuesTuple kco; - auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, kco); - - m_recorder->recordGenericSetResponse(status); - - return status; - } - - return SAI_STATUS_SUCCESS; -} - -sai_status_t RedisRemoteSaiInterface::setRedisPortExtensionAttribute( - _In_ sai_object_type_t objectType, - _In_ sai_object_id_t objectId, - _In_ const sai_attribute_t *attr) -{ - SWSS_LOG_ENTER(); - - if (attr == nullptr) - { - SWSS_LOG_ERROR("attr pointer is null"); - - return SAI_STATUS_INVALID_PARAMETER; - } - - std::string str_attr_id = sai_serialize_redis_port_attr_id( - static_cast(attr->id)); - - switch (attr->id) - { - case SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM: - { - std::string str_attr_value = sai_serialize_redis_link_event_damping_algorithm( - static_cast(attr->value.s32)); - - return setLinkEventDampingConfig( - objectType, objectId, {swss::FieldValueTuple(str_attr_id, str_attr_value)}); - } - case SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG: - { - sai_redis_link_event_damping_algo_aied_config_t *config = - (sai_redis_link_event_damping_algo_aied_config_t *)attr->value.ptr; - - if (config == NULL) - { - SWSS_LOG_ERROR("invalid link damping config attr value NULL"); - - return SAI_STATUS_INVALID_PARAMETER; - } - - std::string str_attr_value = sai_serialize_redis_link_event_damping_aied_config(*config); - - return setLinkEventDampingConfig( - objectType, objectId, {swss::FieldValueTuple(str_attr_id, str_attr_value)}); - } - default: - break; - } - - SWSS_LOG_ERROR("unknown redis port extension attribute: %d", attr->id); - - return SAI_STATUS_INVALID_PARAMETER; -} - bool RedisRemoteSaiInterface::isSaiS8ListValidString( _In_ const sai_s8_list_t &s8list) { @@ -743,11 +666,6 @@ sai_status_t RedisRemoteSaiInterface::set( return setRedisExtensionAttribute(objectType, objectId, attr); } - if (RedisRemoteSaiInterface::isRedisPortAttribute(objectType, attr)) - { - return setRedisPortExtensionAttribute(objectType, objectId, attr); - } - auto status = set( objectType, sai_serialize_object_id(objectId), @@ -2283,20 +2201,6 @@ bool RedisRemoteSaiInterface::isRedisAttribute( return true; } -bool RedisRemoteSaiInterface::isRedisPortAttribute( - _In_ sai_object_id_t objectType, - _In_ const sai_attribute_t* attr) -{ - SWSS_LOG_ENTER(); - - if ((objectType != SAI_OBJECT_TYPE_PORT) || (attr == nullptr) || (attr->id < SAI_PORT_ATTR_CUSTOM_RANGE_START)) - { - return false; - } - - return true; -} - void RedisRemoteSaiInterface::handleNotification( _In_ const std::string &name, _In_ const std::string &serializedNotification, diff --git a/lib/RedisRemoteSaiInterface.h b/lib/RedisRemoteSaiInterface.h index 20ffe4a6ef..74019eccf8 100644 --- a/lib/RedisRemoteSaiInterface.h +++ b/lib/RedisRemoteSaiInterface.h @@ -231,15 +231,6 @@ namespace sairedis _In_ sai_object_id_t switchId, _In_ const sai_attribute_t* attr); - /** - * @brief Checks whether attribute is custom SAI_REDIS_PORT attribute. - * - * This function should only be used on port_api set function. - */ - static bool isRedisPortAttribute( - _In_ sai_object_id_t obejctType, - _In_ const sai_attribute_t* attr); - void setMeta( _In_ std::weak_ptr meta); @@ -410,11 +401,6 @@ namespace sairedis _In_ sai_object_id_t objectId, _In_ const sai_attribute_t *attr); - sai_status_t setRedisPortExtensionAttribute( - _In_ sai_object_type_t objectType, - _In_ sai_object_id_t objectId, - _In_ const sai_attribute_t *attr); - bool isSaiS8ListValidString( _In_ const sai_s8_list_t &s8list); @@ -442,11 +428,6 @@ namespace sairedis _In_ sai_object_id_t switchId, _In_ const sai_attribute_t *attr); - sai_status_t setLinkEventDampingConfig( - _In_ sai_object_type_t objectType, - _In_ sai_object_id_t objectId, - _In_ const std::vector &values); - void clear_local_state(); sai_switch_notifications_t processNotification( diff --git a/lib/Sai.cpp b/lib/Sai.cpp index 03f441e560..22b33e4764 100644 --- a/lib/Sai.cpp +++ b/lib/Sai.cpp @@ -251,12 +251,6 @@ sai_status_t Sai::set( REDIS_CHECK_CONTEXT(objectId); - if (RedisRemoteSaiInterface::isRedisPortAttribute(objectType, attr)) - { - // skip metadata if attribute is redis extension port attribute. - return context->m_redisSai->set(objectType, objectId, attr); - } - return context->m_meta->set(objectType, objectId, attr); } diff --git a/lib/sairediscommon.h b/lib/sairediscommon.h index c2da2a08e5..4594cab5b0 100644 --- a/lib/sairediscommon.h +++ b/lib/sairediscommon.h @@ -52,8 +52,6 @@ #define REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_QUERY "object_type_get_availability_query" #define REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_RESPONSE "object_type_get_availability_response" -#define REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET "link_event_damping_config_set" - #define REDIS_FLEX_COUNTER_COMMAND_START_POLL "start_poll" #define REDIS_FLEX_COUNTER_COMMAND_STOP_POLL "stop_poll" #define REDIS_FLEX_COUNTER_COMMAND_SET_GROUP "set_counter_group" diff --git a/syncd/NotificationProcessor.cpp b/syncd/NotificationProcessor.cpp index 20eb3937cb..1bd5ce0b97 100644 --- a/syncd/NotificationProcessor.cpp +++ b/syncd/NotificationProcessor.cpp @@ -18,10 +18,8 @@ using namespace saimeta; NotificationProcessor::NotificationProcessor( _In_ std::shared_ptr producer, _In_ std::shared_ptr client, - _In_ std::function synchronizer, - _In_ std::function linkEventDampingApplier): + _In_ std::function synchronizer): m_synchronizer(synchronizer), - m_linkEventDampingApplier(linkEventDampingApplier), m_client(client), m_notifications(producer) { @@ -496,9 +494,6 @@ void NotificationProcessor::process_on_port_state_change( SWSS_LOG_DEBUG("port notification count: %u", count); - // Vector to store filtered notifications (after damping applied) - std::vector filtered_notifications; - for (uint32_t i = 0; i < count; i++) { sai_port_oper_status_notification_t *oper_stat = &data[i]; @@ -525,43 +520,14 @@ void NotificationProcessor::process_on_port_state_change( * Port may be in process of removal. OA may receive notification for VID either * SAI_NULL_OBJECT_ID or non exist at time of processing */ - SWSS_LOG_INFO("Port VID %s state change notification: %s", - sai_serialize_object_id(oper_stat->port_id).c_str(), - sai_serialize_port_oper_status(oper_stat->port_state).c_str()); - - // Apply link event damping if configured - bool should_suppress = false; - if (m_linkEventDampingApplier != nullptr && oper_stat->port_id != SAI_NULL_OBJECT_ID) - { - should_suppress = m_linkEventDampingApplier(oper_stat->port_id, oper_stat->port_state); - } - if (!should_suppress) - { - // Add to filtered notifications - filtered_notifications.push_back(*oper_stat); - SWSS_LOG_INFO("Port state change PROPAGATED: %s -> %s", - sai_serialize_object_id(oper_stat->port_id).c_str(), - sai_serialize_port_oper_status(oper_stat->port_state).c_str()); - } - else - { - SWSS_LOG_INFO("Port state change SUPPRESSED by damping: %s -> %s", - sai_serialize_object_id(oper_stat->port_id).c_str(), - sai_serialize_port_oper_status(oper_stat->port_state).c_str()); - } + SWSS_LOG_INFO("Port VID %s state change notification", + sai_serialize_object_id(oper_stat->port_id).c_str()); } - // Send only non-suppressed (filtered) notifications - if (!filtered_notifications.empty()) - { - std::string s = sai_serialize_port_oper_status_ntf((uint32_t)filtered_notifications.size(), filtered_notifications.data()); - sendNotification(SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE, s); - } - else - { - SWSS_LOG_DEBUG("All port state changes were suppressed by damping, no notification sent"); - } + std::string s = sai_serialize_port_oper_status_ntf(count, data); + + sendNotification(SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE, s); } void NotificationProcessor::process_on_bfd_session_state_change( diff --git a/syncd/NotificationProcessor.h b/syncd/NotificationProcessor.h index 4d17748663..3ee4a941cf 100644 --- a/syncd/NotificationProcessor.h +++ b/syncd/NotificationProcessor.h @@ -22,8 +22,7 @@ namespace syncd NotificationProcessor( _In_ std::shared_ptr producer, _In_ std::shared_ptr client, - _In_ std::function synchronizer, - _In_ std::function linkEventDampingApplier = nullptr); + _In_ std::function synchronizer); virtual ~NotificationProcessor(); @@ -212,12 +211,6 @@ namespace syncd std::function m_synchronizer; - /** - * @brief Callback function to apply link event damping to port state changes - * Returns true if notification should be suppressed, false if it should be propagated - */ - std::function m_linkEventDampingApplier; - std::shared_ptr m_client; std::shared_ptr m_notifications; diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index 1af264b611..d2bc0ba056 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -67,8 +67,7 @@ Syncd::Syncd( m_vendorSai(vendorSai), m_veryFirstRun(false), m_enableSyncMode(false), - m_timerWatchdog(cmd->m_watchdogWarnTimeSpan * WD_DELAY_FACTOR), - m_runDampingTimerThread(false) + m_timerWatchdog(cmd->m_watchdogWarnTimeSpan * WD_DELAY_FACTOR) { SWSS_LOG_ENTER(); @@ -135,8 +134,6 @@ Syncd::Syncd( // we need STATE_DB ASIC_DB and COUNTERS_DB m_dbAsic = std::make_shared(m_contextConfig->m_dbAsic, 0); - m_dbState = std::make_shared("STATE_DB", 0); - m_dampingCounterTable = std::make_shared(m_dbState.get(), "LINK_EVENT_DAMPING_STATS"); m_mdioIpcServer = std::make_shared(m_vendorSai, m_commandLineOptions->m_globalContext); if (m_contextConfig->m_zmqEnable) @@ -182,11 +179,7 @@ Syncd::Syncd( m_client = std::make_shared(m_dbAsic); } - m_processor = std::make_shared( - m_notifications, - m_client, - std::bind(&Syncd::syncProcessNotification, this, _1), - std::bind(&Syncd::applyLinkEventDamping, this, _1, _2)); + m_processor = std::make_shared(m_notifications, m_client, std::bind(&Syncd::syncProcessNotification, this, _1)); m_handler = std::make_shared(m_processor); m_sn.onFdbEvent = std::bind(&NotificationHandler::onFdbEvent, m_handler.get(), _1, _2); @@ -270,9 +263,6 @@ Syncd::Syncd( m_breakConfig = BreakConfigParser::parseBreakConfig(m_commandLineOptions->m_breakConfig); - // Start the damping timer thread for proactive timeout enforcement - startDampingTimerThread(); - SWSS_LOG_NOTICE("syncd started"); } @@ -280,8 +270,7 @@ Syncd::~Syncd() { SWSS_LOG_ENTER(); - // Stop the damping timer thread - stopDampingTimerThread(); + // empty } void Syncd::performStartupLogic() @@ -484,9 +473,6 @@ sai_status_t Syncd::processSingleEvent( if (op == REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_QUERY) return processObjectTypeGetAvailabilityQuery(kco); - if (op == REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET) - return processLinkEventDampingConfigSet(kco); - if (op == REDIS_FLEX_COUNTER_COMMAND_START_POLL) return processFlexCounterEvent(key, SET_COMMAND, kfvFieldsValues(kco)); @@ -856,812 +842,6 @@ sai_status_t Syncd::processStatsStCapabilityQuery( return status; } -sai_status_t Syncd::processLinkEventDampingConfigSet( - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - auto& key = kfvKey(kco); - auto& values = kfvFieldsValues(kco); - - // Parse the key format: "OBJECT_TYPE:OBJECT_ID" - size_t colon_pos = key.find(":"); - if (colon_pos == std::string::npos) - { - SWSS_LOG_ERROR("invalid key format: %s", key.c_str()); - sendLinkEventDampingConfigResponse(SAI_STATUS_INVALID_PARAMETER); - return SAI_STATUS_INVALID_PARAMETER; - } - - // Extract object type and object ID - std::string strObjectType = key.substr(0, colon_pos); - std::string strObjectId = key.substr(colon_pos + 1); - - sai_object_type_t objectType; - sai_deserialize_object_type(strObjectType, objectType); - - // Link event damping is a software-based feature - validate port exists - if (objectType != SAI_OBJECT_TYPE_PORT) - { - SWSS_LOG_ERROR("invalid object type for link event damping config: %s", - strObjectType.c_str()); - sendLinkEventDampingConfigResponse(SAI_STATUS_INVALID_PARAMETER); - return SAI_STATUS_INVALID_PARAMETER; - } - - sai_object_id_t portVid; - sai_deserialize_object_id(strObjectId, portVid); - - // Validate that the port exists by translating VID to RID - sai_object_id_t portRid = m_translator->translateVidToRid(portVid); - - if (portRid == SAI_NULL_OBJECT_ID) - { - SWSS_LOG_ERROR("failed to translate port VID to RID"); - sendLinkEventDampingConfigResponse(SAI_STATUS_INVALID_PARAMETER); - return SAI_STATUS_INVALID_PARAMETER; - } - - // Link event damping is a software-based feature implemented in syncd. - // Store the configuration parameters on the port object so that - // OnPortStateChange can apply the damping algorithm before forwarding notifications. - // The damping parameters will be used to decide whether to suppress link state changes. - sai_status_t status = SAI_STATUS_SUCCESS; - - // Acquire lock to protect damping state - std::lock_guard lock(m_linkEventDampingMutex); - - // Get or create damping state for this port - auto& dampingState = m_portLinkEventDampingStates[portVid]; - - // Process each attribute and apply it to the port - for (const auto& v : values) - { - std::string strAttrId = fvField(v); - std::string strAttrValue = fvValue(v); - - SWSS_LOG_DEBUG("processing link event damping attribute: %s = %s", - strAttrId.c_str(), strAttrValue.c_str()); - - // Deserialize attribute ID - sai_redis_port_attr_t attrId; - sai_deserialize_redis_port_attr_id(strAttrId, attrId); - - // Parse and set the attribute value based on the attribute ID - switch (attrId) - { - case SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM: - { - sai_redis_link_event_damping_algorithm_t algo; - sai_deserialize_redis_link_event_damping_algorithm(strAttrValue, algo); - - SWSS_LOG_INFO("setting link event damping algorithm on port %s: %d", - strObjectId.c_str(), algo); - - // Link event damping is a software-only feature as of now - // Store the configuration locally for use in notification - // processing. - dampingState.algorithm = algo; - - status = SAI_STATUS_SUCCESS; - break; - } - - case SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG: - { - // Allocate temporary memory for the config structure - sai_redis_link_event_damping_algo_aied_config_t *config = - new sai_redis_link_event_damping_algo_aied_config_t(); - - sai_deserialize_redis_link_event_damping_aied_config(strAttrValue, *config); - - SWSS_LOG_INFO("setting link event damping AIED config on port %s: " - "max_suppress_time=%u, suppress_threshold=%u, " - "reuse_threshold=%u, decay_half_life=%u, flap_penalty=%u", - strObjectId.c_str(), config->max_suppress_time, - config->suppress_threshold, config->reuse_threshold, - config->decay_half_life, config->flap_penalty); - - // Link event damping is a software-only feature as of now - // Store the configuration locally for use in notification - // processing. - dampingState.aied_config = *config; - - // Free the temporary allocated memory - delete config; - - status = SAI_STATUS_SUCCESS; - break; - } - - default: - { - SWSS_LOG_WARN("unknown attribute ID: %d for link event damping", attrId); - status = SAI_STATUS_INVALID_PARAMETER; - break; - } - } - - if (status != SAI_STATUS_SUCCESS && status != SAI_STATUS_INVALID_PARAMETER) - { - // Log error but continue processing other attributes - SWSS_LOG_WARN("error processing link event damping attribute %s: %s", - strAttrId.c_str(), sai_serialize_status(status).c_str()); - break; - } - } - - sendLinkEventDampingConfigResponse(status); - - return status; -} - -void Syncd::sendLinkEventDampingConfigResponse( - _In_ sai_status_t status) -{ - SWSS_LOG_ENTER(); - - // If sync mode is not enabled, do not send response. - if (!m_enableSyncMode) - { - return; - } - - std::string strStatus = sai_serialize_status(status); - - std::vector entry; - - SWSS_LOG_INFO("sending link event damping config response: %s", strStatus.c_str()); - - m_selectableChannel->set(strStatus, entry, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); -} - -uint64_t Syncd::getCurrentTimeMs() -{ - auto now = std::chrono::system_clock::now(); - auto duration = now.time_since_epoch(); - return std::chrono::duration_cast(duration).count(); -} - -void Syncd::decayPenalty( - _In_ LinkEventDampingPortState& state, - _In_ uint64_t currentTimeMs) -{ - SWSS_LOG_ENTER(); - - if (state.current_penalty == 0) - { - return; // No penalty to decay - } - - if (state.aied_config.decay_half_life == 0) - { - return; // Invalid configuration, skip decay - } - - // Use last_decay_time to track decay independently from state transitions - // This ensures penalty decays even if no link state changes occur - uint64_t base_time = state.last_decay_time_ms; - if (base_time == 0) - { - // First time calculating decay - use last transition time as base - base_time = state.last_transition_time_ms; - } - - // Calculate elapsed time in milliseconds since last decay - uint64_t elapsed_ms = currentTimeMs - base_time; - - if (elapsed_ms <= 0) - { - return; // No time has elapsed - } - - // Penalty decay formula: P(t) = P0 * (0.5 ^ (t / half_life)) - // We use floating point for the calculation - double half_lives = (double)elapsed_ms / state.aied_config.decay_half_life; - double decay_factor = std::pow(0.5, half_lives); - uint32_t decayed_penalty = (uint32_t)(state.current_penalty * decay_factor); - - // Ensure penalty doesn't go below 0 - if (decayed_penalty < state.current_penalty) - { - state.current_penalty = decayed_penalty; - state.last_decay_time_ms = currentTimeMs; // Update last decay time - SWSS_LOG_DEBUG("Port penalty decayed: %u (half_life=%u ms, elapsed=%lu ms, decay_factor=%f)", - state.current_penalty, state.aied_config.decay_half_life, elapsed_ms, decay_factor); - } - else if (state.last_decay_time_ms == 0) - { - // Initialize decay time on first check - state.last_decay_time_ms = currentTimeMs; - } -} - -bool Syncd::applyAiedAlgorithm( - _In_ sai_object_id_t portVid, - _In_ LinkEventDampingPortState& state, - _In_ sai_port_oper_status_t newStatus, - _In_ uint64_t currentTimeMs) -{ - SWSS_LOG_ENTER(); - - std::string portVidStr = sai_serialize_object_id(portVid); - - // Validate configuration - if (state.aied_config.decay_half_life > state.aied_config.max_suppress_time) - { - SWSS_LOG_WARN("Port VID %s invalid damping configuration: " - "decay_half_life (%u ms) > max_suppress_time (%u ms). Damping disabled.", - portVidStr.c_str(), state.aied_config.decay_half_life, - state.aied_config.max_suppress_time); - return false; // Damping disabled for invalid config - } - - // First, apply penalty decay - decayPenalty(state, currentTimeMs); - - // Track if damping was active before this event - bool was_damping_active_before = state.is_damping_active; - - // Check if a link state change occurred - if (state.physical_status != newStatus) - { - // Link state transitioned - state.pre_damping_link_transitions++; - - if (newStatus == SAI_PORT_OPER_STATUS_UP) - { - state.pre_damping_up_events++; - } - else if (newStatus == SAI_PORT_OPER_STATUS_DOWN) - { - state.pre_damping_down_events++; - // Reset damping timer on DOWN event if damping is already active - if (state.is_damping_active) - { - state.damping_start_time_ms = currentTimeMs; - SWSS_LOG_DEBUG("Damping timer reset on DOWN event: new start time = %lu ms", - currentTimeMs); - } - } - - // Add penalty ONLY on DOWN events (UP -> DOWN) - if (state.physical_status == SAI_PORT_OPER_STATUS_UP && newStatus == SAI_PORT_OPER_STATUS_DOWN) - { - state.current_penalty += state.aied_config.flap_penalty; - - // Calculate penalty ceiling: 2^(max_suppress_time/decay_half_life) * reuse_threshold - double exponent = (double)state.aied_config.max_suppress_time / state.aied_config.decay_half_life; - uint32_t penalty_ceiling = (uint32_t)(std::pow(2.0, exponent) * state.aied_config.reuse_threshold); - - if (state.current_penalty > penalty_ceiling) - { - state.current_penalty = penalty_ceiling; - } - - SWSS_LOG_DEBUG("Port DOWN event: penalty accumulated to %u " - "(penalty_ceiling: %u, flap_penalty: %u)", - state.current_penalty, penalty_ceiling, state.aied_config.flap_penalty); - } - else - { - SWSS_LOG_DEBUG("Port UP event: no penalty added (penalty remains: %u)", - state.current_penalty); - } - - // Update physical status and timestamps - state.physical_status = newStatus; - state.last_transition_time_ms = currentTimeMs; - - // If this is the first state change after damping config was set, - // initialize decay time as well - if (state.last_decay_time_ms == 0) - { - state.last_decay_time_ms = currentTimeMs; - } - - // Check if we should enter damping state - if (state.current_penalty >= state.aied_config.suppress_threshold && - !state.is_damping_active) - { - std::string portVidStr = sai_serialize_object_id(portVid); - SWSS_LOG_NOTICE("Port VID %s entering damped state: penalty (%u) >= " - "suppress_threshold (%u) at time %lu ms. Current event will be " - "PROPAGATED, future events will be suppressed.", - portVidStr.c_str(), state.current_penalty, - state.aied_config.suppress_threshold, currentTimeMs); - state.is_damping_active = true; - state.damping_start_time_ms = currentTimeMs; - } - - // Write updated pre-damping counters and physical status to STATE_DB - writeDampingCountersToStateDb(portVid, state); - } - - // Damping exits when EITHER: - // 1. Time-based: damping_duration_ms >= max_suppress_time - // 2. Penalty-based: current_penalty < reuse_threshold (decay-based recovery) - if (state.is_damping_active) - { - // Check timeout - never suppress longer than max_suppress_time - // This is a hard timestamp-based limit to prevent infinite suppression - uint64_t damping_duration_ms = currentTimeMs - state.damping_start_time_ms; - - if (damping_duration_ms >= state.aied_config.max_suppress_time) - { - // Store temporary strings to avoid dangling pointers - std::string physicalStatusStr = sai_serialize_port_oper_status(state.physical_status); - std::string advertisedStatusStr = sai_serialize_port_oper_status(state.advertised_status); - SWSS_LOG_NOTICE("Port VID %s exiting damped state: max suppress time (%u ms) " - "exceeded. Duration: %lu ms. Physical state: %s, Advertised state: %s", - portVidStr.c_str(), state.aied_config.max_suppress_time, - damping_duration_ms, physicalStatusStr.c_str(), - advertisedStatusStr.c_str()); - state.is_damping_active = false; - state.damping_start_time_ms = 0; // Reset timer when exiting damping - - // Propagate last link event when penalty decays below reuse threshold - if (state.advertised_status != state.physical_status) - { - state.pending_state_sync = true; - SWSS_LOG_NOTICE("Port VID %s state mismatch detected on damping " - "exit (timeout): physical=%s, advertised=%s. " - "Marking for state sync on next notification.", - portVidStr.c_str(), physicalStatusStr.c_str(), - advertisedStatusStr.c_str()); - } - state.advertised_status = state.physical_status; - - // Write updated state to STATE_DB after exiting damping - writeDampingCountersToStateDb(portVid, state); - } - // Check reuse threshold - exit if penalty decays below threshold - // Penalty decays based on last_decay_time tracking - else if (state.current_penalty < state.aied_config.reuse_threshold) - { - // Store temporary strings to avoid dangling pointers - std::string physicalStatusStr = sai_serialize_port_oper_status(state.physical_status); - std::string advertisedStatusStr = sai_serialize_port_oper_status(state.advertised_status); - SWSS_LOG_NOTICE("Port VID %s exiting damped state: penalty (%u) < " - "reuse_threshold (%u). Penalty decayed due to exponential decay " - "formula. Physical state: %s, Advertised state: %s", - portVidStr.c_str(), state.current_penalty, - state.aied_config.reuse_threshold, physicalStatusStr.c_str(), - advertisedStatusStr.c_str()); - state.is_damping_active = false; - state.damping_start_time_ms = 0; // Reset timer when exiting damping - - // Propagate last link event when penalty decays below reuse threshold - if (state.advertised_status != state.physical_status) - { - state.pending_state_sync = true; - SWSS_LOG_NOTICE("Port VID %s state mismatch detected on damping " - "exit (decay): physical=%s, advertised=%s. " - "Marking for state sync on next notification.", - portVidStr.c_str(), physicalStatusStr.c_str(), - advertisedStatusStr.c_str()); - } - state.advertised_status = state.physical_status; - - // Write updated state to STATE_DB after exiting damping - writeDampingCountersToStateDb(portVid, state); - } - } - - // Determine if notification should be suppressed - bool should_suppress = false; - - // Only suppress when damping is active AND this is NOT the threshold-crossing event - // The threshold-crossing event itself should be propagated - if (state.is_damping_active && was_damping_active_before) - { - // Calculate current suppression time based on damping algorithm - uint64_t damping_duration_ms = currentTimeMs - state.damping_start_time_ms; - - if (damping_duration_ms < state.aied_config.max_suppress_time) - { - // Calculate expected suppression time - // suppression_time = decay_half_life * log2(reuse_threshold / accumulated_penalty) - if (state.current_penalty > 0 && state.current_penalty >= state.aied_config.reuse_threshold) - { - double suppression_ratio = (double)state.aied_config.reuse_threshold / state.current_penalty; - double expected_suppress_time = state.aied_config.decay_half_life * std::log2(suppression_ratio); - - SWSS_LOG_DEBUG("Port damping active: suppression_time=%.0f ms, " - "max_suppress_time=%u ms, current_penalty=%u, reuse_threshold=%u", - expected_suppress_time, state.aied_config.max_suppress_time, - state.current_penalty, state.aied_config.reuse_threshold); - } - - // Suppress the notification - should_suppress = true; - state.last_suppressed_status = newStatus; // Track what was suppressed - // Store temporary strings to avoid dangling pointers - std::string newStatusStr = sai_serialize_port_oper_status(newStatus); - SWSS_LOG_NOTICE("Port VID %s suppressing port state change notification: " - "new_status=%s (damping active, penalty: %u, duration: %lu ms)", - portVidStr.c_str(), newStatusStr.c_str(), - state.current_penalty, damping_duration_ms); - } - else - { - // Should not happen due to exit check above, but handle gracefully - should_suppress = false; - - SWSS_LOG_WARN("Port VID %s unexpected state: damping_active=true but " - "duration >= max_suppress_time", portVidStr.c_str()); - } - } - else - { - // Damping is NOT active OR this is the threshold-crossing event - propagate the notification - should_suppress = false; - std::string physicalStatusStr = sai_serialize_port_oper_status(state.physical_status); - std::string advertisedStatusStr = sai_serialize_port_oper_status(state.advertised_status); - - // Track advertised transitions - if (newStatus == SAI_PORT_OPER_STATUS_UP) - { - state.post_damping_up_events++; - } - else if (newStatus == SAI_PORT_OPER_STATUS_DOWN) - { - state.post_damping_down_events++; - } - state.post_damping_link_transitions++; - - // SYNC STATE: Update advertised to match new state - state.advertised_status = newStatus; - - // Clear the pending sync flag since state is now synchronized - if (state.pending_state_sync && newStatus == state.physical_status) - { - state.pending_state_sync = false; - SWSS_LOG_INFO("Port state sync completed: physical=%s, advertised=%s", - physicalStatusStr.c_str(), advertisedStatusStr.c_str()); - } - - // Write updated counters to STATE_DB - writeDampingCountersToStateDb(portVid, state); - - // When damping changes state (enabled->disabled), propagate the event - if (was_damping_active_before && !state.is_damping_active) - { - SWSS_LOG_NOTICE("Port VID %s state change PROPAGATED (damping state changed " - "from active to inactive): physical=%s, advertised=%s", - portVidStr.c_str(), physicalStatusStr.c_str(), - advertisedStatusStr.c_str()); - } - else - { - SWSS_LOG_INFO("Port VID %s state change PROPAGATED: physical=%s, advertised=%s (damping inactive)", - portVidStr.c_str(), physicalStatusStr.c_str(), - advertisedStatusStr.c_str()); - } - - } - - return should_suppress; -} - -bool Syncd::applyLinkEventDamping( - _In_ sai_object_id_t portVid, - _In_ sai_port_oper_status_t newStatus) -{ - SWSS_LOG_ENTER(); - - std::lock_guard lock(m_linkEventDampingMutex); - - // Check if damping is configured for this port - auto it = m_portLinkEventDampingStates.find(portVid); - if (it == m_portLinkEventDampingStates.end()) - { - // No damping configured for this port - return false; // Don't suppress - } - - LinkEventDampingPortState& state = it->second; - - // Check if damping algorithm is enabled - if (state.algorithm == SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED) - { - return false; // Damping disabled - } - - uint64_t currentTimeMs = getCurrentTimeMs(); - - // Apply the appropriate damping algorithm - switch (state.algorithm) - { - case SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED: - return applyAiedAlgorithm(portVid, state, newStatus, currentTimeMs); - - default: - SWSS_LOG_WARN("Unknown damping algorithm: %d", state.algorithm); - return false; - } -} - -void Syncd::checkDampedPortsTimeout() -{ - SWSS_LOG_ENTER(); - - std::lock_guard lock(m_linkEventDampingMutex); - - uint64_t currentTimeMs = getCurrentTimeMs(); - std::vector> portsToSync; - - // Iterate through all ports with damping configured - for (auto& kv : m_portLinkEventDampingStates) - { - auto& portVid = kv.first; - auto& state = kv.second; - - // Only check ports that are currently in damped state - if (!state.is_damping_active) - { - continue; - } - - // Check if damping algorithm is enabled - if (state.algorithm != SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED) - { - continue; - } - - std::string portVidStr = sai_serialize_object_id(portVid); - std::string physicalStatusStr = sai_serialize_port_oper_status(state.physical_status); - std::string advertisedStatusStr = sai_serialize_port_oper_status(state.advertised_status); - // Apply penalty decay first - penalty naturally decays over time - decayPenalty(state, currentTimeMs); - - // Check if penalty has decayed below reuse threshold - if (state.current_penalty < state.aied_config.reuse_threshold) - { - SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s exiting damped state: " - "penalty (%u) < reuse_threshold (%u). Penalty decayed due to " - "exponential decay formula. Physical state: %s, Advertised state: %s", - portVidStr.c_str(), state.current_penalty, - state.aied_config.reuse_threshold, physicalStatusStr.c_str(), - advertisedStatusStr.c_str()); - - // Exit damping state - state.is_damping_active = false; - state.damping_start_time_ms = 0; // Reset timer when exiting damping - - // Check if there's a state mismatch that needs to be propagated - if (state.advertised_status != state.physical_status) - { - SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s state mismatch " - "detected on damping exit (decay): " - "physical=%s, advertised=%s. Will send notification.", - portVidStr.c_str(), physicalStatusStr.c_str(), - advertisedStatusStr.c_str()); - - // Update advertised status to match physical - state.advertised_status = state.physical_status; - state.pending_state_sync = false; - - // Collect port info for notification - portsToSync.push_back(std::make_pair(portVid, state.physical_status)); - } - else - { - SWSS_LOG_INFO("Proactive timeout check: Port VID %s exited damping " - "with no state mismatch.", portVidStr.c_str()); - } - - // Write updated state to STATE_DB after exiting damping - writeDampingCountersToStateDb(portVid, state); - - // Skip to next port since we've already handled this one - continue; - } - - // Calculate how long the port has been damped - uint64_t damping_duration_ms = currentTimeMs - state.damping_start_time_ms; - - // Check if max_suppress_time has been exceeded - if (damping_duration_ms >= state.aied_config.max_suppress_time) - { - SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s exiting damped " - "state: max suppress time (%u ms) exceeded. " - "Duration: %lu ms. Physical state: %s, Advertised state: %s", - portVidStr.c_str(), state.aied_config.max_suppress_time, - damping_duration_ms, physicalStatusStr.c_str(), - advertisedStatusStr.c_str()); - - // Exit damping state - state.is_damping_active = false; - state.damping_start_time_ms = 0; // Reset timer when exiting damping - - // Check if there's a state mismatch that needs to be propagated - if (state.advertised_status != state.physical_status) - { - SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s state mismatch detected on damping exit: " - "physical=%s, advertised=%s. Will send notification.", - portVidStr.c_str(), physicalStatusStr.c_str(), - advertisedStatusStr.c_str()); - - // Update advertised status to match physical - state.advertised_status = state.physical_status; - state.pending_state_sync = false; - - // Collect port info for notification - portsToSync.push_back(std::make_pair(portVid, state.physical_status)); - } - else - { - SWSS_LOG_INFO("Proactive timeout check: Port VID %s exited damping " - "with no state mismatch.", portVidStr.c_str()); - } - - // Write updated state to STATE_DB after exiting damping - writeDampingCountersToStateDb(portVid, state); - } - else - { - // Port is still in damped state - write updated stats to STATE_DB - // to reflect the decayed penalty value in real-time - writeDampingCountersToStateDb(portVid, state); - - SWSS_LOG_DEBUG("Proactive timeout check: Port VID %s still damped: " - "penalty=%u, duration=%lu ms", portVidStr.c_str(), - state.current_penalty, damping_duration_ms); - } - } - - // Release the lock before sending notifications - // Note: We make a copy of the port list above to avoid holding the lock during notification send - // Send notifications for ports that need state synchronization - if (!portsToSync.empty()) - { - SWSS_LOG_NOTICE("Proactive timeout check: Sending %zu port state notifications " - "after damping timeout", portsToSync.size()); - - // Send each port notification through the notification system - for (const auto& kv : portsToSync) - { - const auto& portVid = kv.first; - const auto& status = kv.second; - - std::string portVidStr = sai_serialize_object_id(portVid); - std::string statusStr = sai_serialize_port_oper_status(status); - - // Build notification data - sai_port_oper_status_notification_t notification; - notification.port_id = portVid; - notification.port_state = status; - - std::string serialized = sai_serialize_port_oper_status_ntf(1, ¬ification); - - // Send directly through the notification producer - std::vector entry; - m_notifications->send(SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE, - serialized, entry); - SWSS_LOG_NOTICE("Proactive timeout check: Sent notification for Port VID %s -> %s", - portVidStr.c_str(), statusStr.c_str()); - } - } -} - -void Syncd::dampingTimerThreadFunc() -{ - SWSS_LOG_ENTER(); - SWSS_LOG_NOTICE("Damping timer thread started"); - - while (true) - { - { - std::unique_lock lock(m_dampingTimerMutex); - - // Wait for 1 second or until signaled to stop - if (m_dampingTimerCv.wait_for(lock, std::chrono::seconds(1), [this] { return !m_runDampingTimerThread; })) - { - // Signaled to stop - SWSS_LOG_NOTICE("Damping timer thread received stop signal"); - break; - } - - // Check if still running (in case of spurious wakeup) - if (!m_runDampingTimerThread) - { - break; - } - } - - // Perform the proactive timeout check - try - { - checkDampedPortsTimeout(); - } - catch (const std::exception& e) - { - SWSS_LOG_ERROR("Exception in damping timer thread: %s", e.what()); - } - catch (...) - { - SWSS_LOG_ERROR("Unknown exception in damping timer thread"); - } - } - - SWSS_LOG_NOTICE("Damping timer thread stopped"); -} - -void Syncd::startDampingTimerThread() -{ - SWSS_LOG_ENTER(); - - if (m_runDampingTimerThread) - { - SWSS_LOG_WARN("Damping timer thread already running"); - return; - } - - m_runDampingTimerThread = true; - m_dampingTimerThread = std::make_shared(&Syncd::dampingTimerThreadFunc, this); - - SWSS_LOG_NOTICE("Started damping timer thread for proactive max_suppress_time enforcement"); -} - -void Syncd::stopDampingTimerThread() -{ - SWSS_LOG_ENTER(); - - if (!m_runDampingTimerThread) - { - SWSS_LOG_INFO("Damping timer thread not running"); - return; - } - - // Signal the thread to stop - { - std::lock_guard lock(m_dampingTimerMutex); - m_runDampingTimerThread = false; - } - m_dampingTimerCv.notify_one(); - - // Wait for the thread to finish - if (m_dampingTimerThread && m_dampingTimerThread->joinable()) - { - m_dampingTimerThread->join(); - SWSS_LOG_NOTICE("Damping timer thread stopped and joined"); - } - - m_dampingTimerThread.reset(); -} - -void Syncd::writeDampingCountersToStateDb( - _In_ sai_object_id_t portVid, - _In_ const LinkEventDampingPortState& state) -{ - SWSS_LOG_ENTER(); - - // Convert VID to string for STATE_DB key - std::string portVidStr = sai_serialize_object_id(portVid); - - // Prepare counter fields - std::vector fields; - fields.emplace_back("pre_damping_link_transitions", std::to_string(state.pre_damping_link_transitions)); - fields.emplace_back("pre_damping_up_events", std::to_string(state.pre_damping_up_events)); - fields.emplace_back("pre_damping_down_events", std::to_string(state.pre_damping_down_events)); - fields.emplace_back("post_damping_up_events", std::to_string(state.post_damping_up_events)); - fields.emplace_back("post_damping_down_events", std::to_string(state.post_damping_down_events)); - fields.emplace_back("post_damping_link_transitions", std::to_string(state.post_damping_link_transitions)); - - // Add damping state information - fields.emplace_back("is_damping_active", state.is_damping_active ? "true" : "false"); - fields.emplace_back("current_penalty", std::to_string(state.current_penalty)); - fields.emplace_back("damping_start_time_ms", std::to_string(state.damping_start_time_ms)); - fields.emplace_back("physical_status", sai_serialize_port_oper_status(state.physical_status)); - fields.emplace_back("advertised_status", sai_serialize_port_oper_status(state.advertised_status)); - - // Write to STATE_DB - m_dampingCounterTable->set(portVidStr, fields); - - SWSS_LOG_DEBUG("Wrote damping counters to STATE_DB for port %s", portVidStr.c_str()); -} - sai_status_t Syncd::processFdbFlush( _In_ const swss::KeyOpFieldsValuesTuple &kco) { diff --git a/syncd/Syncd.cpp.orig b/syncd/Syncd.cpp.orig deleted file mode 100644 index d2bc0ba056..0000000000 --- a/syncd/Syncd.cpp.orig +++ /dev/null @@ -1,6105 +0,0 @@ -#include "Syncd.h" -#include "VidManager.h" -#include "NotificationHandler.h" -#include "Workaround.h" -#include "ComparisonLogic.h" -#include "HardReiniter.h" -#include "RedisClient.h" -#include "DisabledRedisClient.h" -#include "RequestShutdown.h" -#include "WarmRestartTable.h" -#include "ContextConfigContainer.h" -#include "BreakConfigParser.h" -#include "RedisNotificationProducer.h" -#include "ZeroMQNotificationProducer.h" -#include "WatchdogScope.h" -#include "VendorSaiOptions.h" - -#include "sairediscommon.h" - -#include "swss/logger.h" -#include "swss/select.h" -#include "swss/tokenize.h" -#include "swss/notificationproducer.h" -#include "swss/exec.h" -#include "swss/dbconnector.h" -#include "swss/table.h" - -#include "meta/sai_serialize.h" -#include "meta/ZeroMQSelectableChannel.h" -#include "meta/RedisSelectableChannel.h" -#include "meta/PerformanceIntervalTimer.h" -#include "meta/Globals.h" - -#include "vslib/saivs.h" - -#include "config.h" - -#include -#include - -#include -#include - -#define DEF_SAI_WARM_BOOT_DATA_FILE "/var/warmboot/sai-warmboot.bin" -#define SAI_FAILURE_DUMP_SCRIPT "/usr/bin/sai_failure_dump.sh" -#define SYNCD_ZMQ_RESPONSE_BUFFER_SIZE (128*1024*1024) - -using namespace syncd; -using namespace saimeta; -using namespace sairediscommon; -using namespace std::placeholders; - -#ifdef ASAN_ENABLED -#define WD_DELAY_FACTOR 2 -#else -#define WD_DELAY_FACTOR 1 -#endif - -Syncd::Syncd( - _In_ std::shared_ptr vendorSai, - _In_ std::shared_ptr cmd, - _In_ bool isWarmStart): - m_commandLineOptions(cmd), - m_isWarmStart(isWarmStart), - m_firstInitWasPerformed(false), - m_asicInitViewMode(false), // by default we are in APPLY view mode - m_vendorSai(vendorSai), - m_veryFirstRun(false), - m_enableSyncMode(false), - m_timerWatchdog(cmd->m_watchdogWarnTimeSpan * WD_DELAY_FACTOR) -{ - SWSS_LOG_ENTER(); - - SWSS_LOG_NOTICE("sairedis git revision %s, SAI git revision: %s", SAIREDIS_GIT_REVISION, SAI_GIT_REVISION); - - SWSS_LOG_NOTICE("command line: %s", m_commandLineOptions->getCommandLineString().c_str()); - - auto ccc = sairedis::ContextConfigContainer::loadFromFile(m_commandLineOptions->m_contextConfig.c_str()); - - m_contextConfig = ccc->get(m_commandLineOptions->m_globalContext); - - if (m_contextConfig == nullptr) - { - SWSS_LOG_THROW("no context config defined at global context %u", m_commandLineOptions->m_globalContext); - } - - if (m_commandLineOptions->m_enableSyncMode - && !(m_contextConfig->m_loadedFromJson && m_contextConfig->m_zmqEnable)) - { - SWSS_LOG_WARN("enable sync mode is deprecated, please use communication mode, FORCING redis sync mode"); - - m_enableSyncMode = true; - - m_contextConfig->m_zmqEnable = false; - - m_commandLineOptions->m_redisCommunicationMode = SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC; - } - - if (m_commandLineOptions->m_redisCommunicationMode == SAI_REDIS_COMMUNICATION_MODE_ZMQ_SYNC) - { - // If context_config.json explicitly set zmq_enable=false, - // respect it and fall back to Redis sync - if (m_contextConfig->m_loadedFromJson && !m_contextConfig->m_zmqEnable) - { - SWSS_LOG_NOTICE("context %u: zmq_enable=false in context config, falling back to Redis sync", - m_contextConfig->m_guid); - - m_enableSyncMode = true; - - m_commandLineOptions->m_redisCommunicationMode = SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC; - } - else - { - SWSS_LOG_NOTICE("zmq sync mode enabled via cmd line for context %u", m_contextConfig->m_guid); - - m_contextConfig->m_zmqEnable = true; - - m_enableSyncMode = true; - } - } - - auto vso = std::make_shared(); - - vso->m_checkAttrVersion = m_commandLineOptions->m_enableAttrVersionCheck; - - m_vendorSai->setOptions(VendorSaiOptions::OPTIONS_KEY, vso); - - m_manager = std::make_shared(m_vendorSai, m_contextConfig->m_dbCounters, m_commandLineOptions->m_supportingBulkCounterGroups); - - loadProfileMap(); - - m_profileIter = m_profileMap.begin(); - - // we need STATE_DB ASIC_DB and COUNTERS_DB - - m_dbAsic = std::make_shared(m_contextConfig->m_dbAsic, 0); - m_mdioIpcServer = std::make_shared(m_vendorSai, m_commandLineOptions->m_globalContext); - - if (m_contextConfig->m_zmqEnable) - { - m_notifications = std::make_shared(m_contextConfig->m_zmqNtfEndpoint); - - SWSS_LOG_NOTICE("zmq enabled, forcing sync mode"); - - m_enableSyncMode = true; - - m_selectableChannel = std::make_shared(m_contextConfig->m_zmqEndpoint, SYNCD_ZMQ_RESPONSE_BUFFER_SIZE); - } - else - { - m_notifications = std::make_shared(m_contextConfig->m_dbAsic); - - m_enableSyncMode = m_commandLineOptions->m_redisCommunicationMode == SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC; - - bool modifyRedis = m_enableSyncMode ? false : true; - - m_selectableChannel = std::make_shared( - m_dbAsic, - ASIC_STATE_TABLE, - REDIS_TABLE_GETRESPONSE, - TEMP_PREFIX, - modifyRedis); - } - - bool isVirtualSwitch = m_profileMap.find(SAI_KEY_VS_SWITCH_TYPE) != m_profileMap.end(); - swss::DBConnector configDb("CONFIG_DB", 0); - swss::Table deviceMetadataTable(&configDb, "DEVICE_METADATA"); - std::string switchType; - deviceMetadataTable.hget("localhost", "switch_type", switchType); - - bool isDpuSwitch = switchType == "dpu"; - - if (m_contextConfig->m_zmqEnable && isDpuSwitch && !isVirtualSwitch) - { - m_client = std::make_shared(); - } - else - { - m_client = std::make_shared(m_dbAsic); - } - - m_processor = std::make_shared(m_notifications, m_client, std::bind(&Syncd::syncProcessNotification, this, _1)); - m_handler = std::make_shared(m_processor); - - m_sn.onFdbEvent = std::bind(&NotificationHandler::onFdbEvent, m_handler.get(), _1, _2); - m_sn.onNatEvent = std::bind(&NotificationHandler::onNatEvent, m_handler.get(), _1, _2); - m_sn.onPortStateChange = std::bind(&NotificationHandler::onPortStateChange, m_handler.get(), _1, _2); - m_sn.onQueuePfcDeadlock = std::bind(&NotificationHandler::onQueuePfcDeadlock, m_handler.get(), _1, _2); - m_sn.onSwitchAsicSdkHealthEvent = std::bind(&NotificationHandler::onSwitchAsicSdkHealthEvent, m_handler.get(), _1, _2, _3, _4, _5, _6); - m_sn.onSwitchShutdownRequest = std::bind(&NotificationHandler::onSwitchShutdownRequest, m_handler.get(), _1); - m_sn.onSwitchStateChange = std::bind(&NotificationHandler::onSwitchStateChange, m_handler.get(), _1, _2); - m_sn.onBfdSessionStateChange = std::bind(&NotificationHandler::onBfdSessionStateChange, m_handler.get(), _1, _2); - m_sn.onIcmpEchoSessionStateChange = std::bind(&NotificationHandler::onIcmpEchoSessionStateChange, m_handler.get(), _1, _2); - m_sn.onPortHostTxReady = std::bind(&NotificationHandler::onPortHostTxReady, m_handler.get(), _1, _2, _3); - m_sn.onTwampSessionEvent = std::bind(&NotificationHandler::onTwampSessionEvent, m_handler.get(), _1, _2); - m_sn.onTamTelTypeConfigChange = std::bind(&NotificationHandler::onTamTelTypeConfigChange, m_handler.get(), _1); - m_sn.onSwitchMacsecPostStatus = std::bind(&NotificationHandler::onSwitchMacsecPostStatus, m_handler.get(), _1, _2); - m_sn.onMacsecPostStatus = std::bind(&NotificationHandler::onMacsecPostStatus, m_handler.get(), _1, _2); - m_sn.onHaSetEvent = std::bind(&NotificationHandler::onHaSetEvent, m_handler.get(), _1, _2); - m_sn.onHaScopeEvent = std::bind(&NotificationHandler::onHaScopeEvent, m_handler.get(), _1, _2); - m_sn.onFlowBulkGetSessionEvent = std::bind(&NotificationHandler::onFlowBulkGetSessionEvent, m_handler.get(), _1, _2, _3); - - m_handler->setSwitchNotifications(m_sn.getSwitchNotifications()); - - m_restartQuery = std::make_shared(m_dbAsic.get(), SYNCD_NOTIFICATION_CHANNEL_RESTARTQUERY_PER_DB(m_contextConfig->m_dbAsic)); - - // TODO to be moved to ASIC_DB - m_dbFlexCounter = std::make_shared(m_contextConfig->m_dbFlex, 0); - m_flexCounter = std::make_shared(m_dbFlexCounter.get(), FLEX_COUNTER_TABLE); - m_flexCounterGroup = std::make_shared(m_dbFlexCounter.get(), FLEX_COUNTER_GROUP_TABLE); - m_flexCounterTable = std::make_shared(m_dbFlexCounter.get(), FLEX_COUNTER_TABLE); - m_flexCounterGroupTable = std::make_shared(m_dbFlexCounter.get(), FLEX_COUNTER_GROUP_TABLE); - - m_switchConfigContainer = std::make_shared(); - m_redisVidIndexGenerator = std::make_shared(m_dbAsic, REDIS_KEY_VIDCOUNTER); - - m_virtualObjectIdManager = - std::make_shared( - m_commandLineOptions->m_globalContext, - m_switchConfigContainer, - m_redisVidIndexGenerator); - - // TODO move to syncd object - m_translator = std::make_shared(m_client, m_virtualObjectIdManager, vendorSai); - - m_processor->m_translator = m_translator; // TODO as param - - m_veryFirstRun = isVeryFirstRun(); - - performStartupLogic(); - - m_smt.profileGetValue = std::bind(&Syncd::profileGetValue, this, _1, _2); - m_smt.profileGetNextValue = std::bind(&Syncd::profileGetNextValue, this, _1, _2, _3); - - m_test_services = m_smt.getServiceMethodTable(); - - sai_status_t status = vendorSai->apiInitialize(0, &m_test_services); - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("FATAL: failed to sai_api_initialize: %s", - sai_serialize_status(status).c_str()); - - abort(); - } - - setSaiApiLogLevel(); - - sai_api_version_t apiVersion = SAI_VERSION(0,0,0); // invalid version - - status = m_vendorSai->queryApiVersion(&apiVersion); - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_WARN("failed to obtain libsai api version: %s", sai_serialize_status(status).c_str()); - } - else - { - SWSS_LOG_NOTICE("libsai api version: %lu", apiVersion); - } - - m_handler->setApiVersion(apiVersion); - - m_breakConfig = BreakConfigParser::parseBreakConfig(m_commandLineOptions->m_breakConfig); - - SWSS_LOG_NOTICE("syncd started"); -} - -Syncd::~Syncd() -{ - SWSS_LOG_ENTER(); - - // empty -} - -void Syncd::performStartupLogic() -{ - SWSS_LOG_ENTER(); - // ignore warm logic here if syncd starts in fast-boot, express-boot or Mellanox fastfast boot mode - - if (m_isWarmStart && m_commandLineOptions->m_startType != SAI_START_TYPE_FASTFAST_BOOT && - m_commandLineOptions->m_startType != SAI_START_TYPE_EXPRESS_BOOT && - m_commandLineOptions->m_startType != SAI_START_TYPE_FAST_BOOT) - { - SWSS_LOG_WARN("override command line startType=%s via SAI_START_TYPE_WARM_BOOT", - CommandLineOptions::startTypeToString(m_commandLineOptions->m_startType).c_str()); - - m_commandLineOptions->m_startType = SAI_START_TYPE_WARM_BOOT; - } - - if (m_commandLineOptions->m_startType == SAI_START_TYPE_WARM_BOOT) - { - const char *warmBootReadFile = profileGetValue(0, SAI_KEY_WARM_BOOT_READ_FILE); - - SWSS_LOG_NOTICE("using warmBootReadFile: '%s'", warmBootReadFile); - - if (warmBootReadFile == NULL || access(warmBootReadFile, F_OK) == -1) - { - SWSS_LOG_WARN("user requested warmStart but warmBootReadFile is not specified or not accessible, forcing cold start"); - - m_commandLineOptions->m_startType = SAI_START_TYPE_COLD_BOOT; - } - } - - if (m_commandLineOptions->m_startType == SAI_START_TYPE_WARM_BOOT && m_veryFirstRun) - { - SWSS_LOG_WARN("warm start requested, but this is very first syncd start, forcing cold start"); - - /* - * We force cold start since if it's first run then redis db is not - * complete so redis asic view will not reflect warm boot asic state, - * if this happen then orch agent needs to be restarted as well to - * repopulate asic view. - */ - - m_commandLineOptions->m_startType = SAI_START_TYPE_COLD_BOOT; - } - - if (m_commandLineOptions->m_startType == SAI_START_TYPE_FASTFAST_BOOT) - { - /* - * Mellanox SAI requires to pass SAI_WARM_BOOT as SAI_BOOT_KEY - * to start 'fastfast' - */ - - m_profileMap[SAI_KEY_BOOT_TYPE] = std::to_string(SAI_START_TYPE_WARM_BOOT); - } - else - { - m_profileMap[SAI_KEY_BOOT_TYPE] = std::to_string(m_commandLineOptions->m_startType); // number value is needed - } -} - -bool Syncd::getAsicInitViewMode() const -{ - SWSS_LOG_ENTER(); - - return m_asicInitViewMode; -} - -void Syncd::setAsicInitViewMode( - _In_ bool enable) -{ - SWSS_LOG_ENTER(); - - m_asicInitViewMode = enable; -} - -bool Syncd::isInitViewMode() const -{ - SWSS_LOG_ENTER(); - - return m_asicInitViewMode && m_commandLineOptions->m_enableTempView; -} - -void Syncd::processEvent( - _In_ sairedis::SelectableChannel& consumer) -{ - SWSS_LOG_ENTER(); - - std::lock_guard lock(m_mutex); - - do - { - swss::KeyOpFieldsValuesTuple kco; - - /* - * In init mode we put all data to TEMP view and we snoop. We need - * to specify temporary view prefix in consumer since consumer puts - * data to redis db. - */ - - consumer.pop(kco, isInitViewMode()); - - processSingleEvent(kco); - } - while (!consumer.empty()); -} - -void Syncd::processEventInShutdownWaitMode( - _In_ sairedis::SelectableChannel& consumer) -{ - SWSS_LOG_ENTER(); - - // Syncd in shutdown-wait mode must respond to INIT_VIEW with FAILURE to avoid deadlock with OA - // This could happen because Orchagent sends INIT_VIEW before registering shutdown callback - // Can't reorder due to circular dependency: need switch to register callbacks, but - // need INIT_VIEW before creating switch - do - { - swss::KeyOpFieldsValuesTuple kco; - consumer.pop(kco, false); - - auto& op = kfvOp(kco); - auto& key = kfvKey(kco); - - SWSS_LOG_WARN("Received command while in shutdown-wait mode: op=%s, key=%s.", op.c_str(), key.c_str()); - - if (op == REDIS_ASIC_STATE_COMMAND_NOTIFY) - { - SWSS_LOG_ERROR("Syncd is waiting for shutdown, cannot process %s: Sending FAILURE response.", key.c_str()); - sendNotifyResponse(SAI_STATUS_FAILURE); - } - else - { - SWSS_LOG_WARN("Ignoring non-notify command in shutdown-wait mode"); - } - } - while (!consumer.empty()); -} - -sai_status_t Syncd::processSingleEvent( - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - auto& key = kfvKey(kco); - auto& op = kfvOp(kco); - - SWSS_LOG_INFO("key: %s op: %s", key.c_str(), op.c_str()); - - if (key.length() == 0) - { - SWSS_LOG_DEBUG("no elements in m_buffer"); - - return SAI_STATUS_SUCCESS; - } - - WatchdogScope ws(m_timerWatchdog, op + ":" + key, &kco); - - if (op == REDIS_ASIC_STATE_COMMAND_CREATE) - return processQuadEvent(SAI_COMMON_API_CREATE, kco); - - if (op == REDIS_ASIC_STATE_COMMAND_REMOVE) - return processQuadEvent(SAI_COMMON_API_REMOVE, kco); - - if (op == REDIS_ASIC_STATE_COMMAND_SET) - return processQuadEvent(SAI_COMMON_API_SET, kco); - - if (op == REDIS_ASIC_STATE_COMMAND_GET) - return processQuadEvent(SAI_COMMON_API_GET, kco); - - if (op == REDIS_ASIC_STATE_COMMAND_BULK_CREATE) - return processBulkQuadEvent(SAI_COMMON_API_BULK_CREATE, kco); - - if (op == REDIS_ASIC_STATE_COMMAND_BULK_REMOVE) - return processBulkQuadEvent(SAI_COMMON_API_BULK_REMOVE, kco); - - if (op == REDIS_ASIC_STATE_COMMAND_BULK_SET) - return processBulkQuadEvent(SAI_COMMON_API_BULK_SET, kco); - - if (op == REDIS_ASIC_STATE_COMMAND_BULK_GET) - return processBulkQuadEvent(SAI_COMMON_API_BULK_GET, kco); - - if (op == REDIS_ASIC_STATE_COMMAND_NOTIFY) - return processNotifySyncd(kco); - - if (op == REDIS_ASIC_STATE_COMMAND_GET_STATS) - return processGetStatsEvent(kco); - - if (op == REDIS_ASIC_STATE_COMMAND_CLEAR_STATS) - return processClearStatsEvent(kco); - - if (op == REDIS_ASIC_STATE_COMMAND_FLUSH) - return processFdbFlush(kco); - - if (op == REDIS_ASIC_STATE_COMMAND_ATTR_CAPABILITY_QUERY) - return processAttrCapabilityQuery(kco); - - if (op == REDIS_ASIC_STATE_COMMAND_ATTR_ENUM_VALUES_CAPABILITY_QUERY) - return processAttrEnumValuesCapabilityQuery(kco); - - if (op == REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_QUERY) - return processObjectTypeGetAvailabilityQuery(kco); - - if (op == REDIS_FLEX_COUNTER_COMMAND_START_POLL) - return processFlexCounterEvent(key, SET_COMMAND, kfvFieldsValues(kco)); - - if (op == REDIS_FLEX_COUNTER_COMMAND_STOP_POLL) - return processFlexCounterEvent(key, DEL_COMMAND, kfvFieldsValues(kco)); - - if (op == REDIS_FLEX_COUNTER_COMMAND_SET_GROUP) - return processFlexCounterGroupEvent(key, SET_COMMAND, kfvFieldsValues(kco)); - - if (op == REDIS_FLEX_COUNTER_COMMAND_DEL_GROUP) - return processFlexCounterGroupEvent(key, DEL_COMMAND, kfvFieldsValues(kco)); - - if (op == REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_QUERY) - return processStatsCapabilityQuery(kco); - - if (op == REDIS_ASIC_STATE_COMMAND_STATS_ST_CAPABILITY_QUERY) - return processStatsStCapabilityQuery(kco); - - SWSS_LOG_THROW("event op '%s' is not implemented, FIXME", op.c_str()); -} - -sai_status_t Syncd::processAttrCapabilityQuery( - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - auto& strSwitchVid = kfvKey(kco); - - sai_object_id_t switchVid; - sai_deserialize_object_id(strSwitchVid, switchVid); - - sai_object_id_t switchRid = m_translator->translateVidToRid(switchVid); - - auto& values = kfvFieldsValues(kco); - - if (values.size() != 2) - { - SWSS_LOG_ERROR("Invalid input: expected 2 arguments, received %zu", values.size()); - - m_selectableChannel->set(sai_serialize_status(SAI_STATUS_INVALID_PARAMETER), {}, REDIS_ASIC_STATE_COMMAND_ATTR_CAPABILITY_RESPONSE); - - return SAI_STATUS_INVALID_PARAMETER; - } - - sai_object_type_t objectType; - sai_deserialize_object_type(fvValue(values[0]), objectType); - - sai_attr_id_t attrId; - sai_deserialize_attr_id(fvValue(values[1]), attrId); - - sai_attr_capability_t capability; - - sai_status_t status = m_vendorSai->queryAttributeCapability(switchRid, objectType, attrId, &capability); - - std::vector entry; - - if (status == SAI_STATUS_SUCCESS) - { - entry = - { - swss::FieldValueTuple("CREATE_IMPLEMENTED", (capability.create_implemented ? "true" : "false")), - swss::FieldValueTuple("SET_IMPLEMENTED", (capability.set_implemented ? "true" : "false")), - swss::FieldValueTuple("GET_IMPLEMENTED", (capability.get_implemented ? "true" : "false")) - }; - - SWSS_LOG_INFO("Sending response: create_implemented:%d, set_implemented:%d, get_implemented:%d", - capability.create_implemented, capability.set_implemented, capability.get_implemented); - } - - m_selectableChannel->set(sai_serialize_status(status), entry, REDIS_ASIC_STATE_COMMAND_ATTR_CAPABILITY_RESPONSE); - - return status; -} - -sai_status_t Syncd::processAttrEnumValuesCapabilityQuery( - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - auto& strSwitchVid = kfvKey(kco); - - sai_object_id_t switchVid; - sai_deserialize_object_id(strSwitchVid, switchVid); - - sai_object_id_t switchRid = m_translator->translateVidToRid(switchVid); - - auto& values = kfvFieldsValues(kco); - - if (values.size() != 3) - { - SWSS_LOG_ERROR("Invalid input: expected 3 arguments, received %zu", values.size()); - - m_selectableChannel->set(sai_serialize_status(SAI_STATUS_INVALID_PARAMETER), {}, REDIS_ASIC_STATE_COMMAND_ATTR_ENUM_VALUES_CAPABILITY_RESPONSE); - - return SAI_STATUS_INVALID_PARAMETER; - } - - sai_object_type_t objectType; - sai_deserialize_object_type(fvValue(values[0]), objectType); - - sai_attr_id_t attrId; - sai_deserialize_attr_id(fvValue(values[1]), attrId); - - uint32_t list_size = std::stoi(fvValue(values[2])); - - std::vector enum_capabilities_list(list_size); - - sai_s32_list_t enumCapList; - - enumCapList.count = list_size; - enumCapList.list = enum_capabilities_list.data(); - - sai_status_t status = m_vendorSai->queryAttributeEnumValuesCapability(switchRid, objectType, attrId, &enumCapList); - - std::vector entry; - - if (status == SAI_STATUS_SUCCESS) - { - std::vector vec; - std::transform(enumCapList.list, enumCapList.list + enumCapList.count, - std::back_inserter(vec), [](auto&e) { return std::to_string(e); }); - - std::ostringstream join; - std::copy(vec.begin(), vec.end(), std::ostream_iterator(join, ",")); - - auto strCap = join.str(); - - entry = - { - swss::FieldValueTuple("ENUM_CAPABILITIES", strCap), - swss::FieldValueTuple("ENUM_COUNT", std::to_string(enumCapList.count)) - }; - - SWSS_LOG_DEBUG("Sending response: capabilities = '%s', count = %d", strCap.c_str(), enumCapList.count); - } - else if (status == SAI_STATUS_BUFFER_OVERFLOW) - { - entry = - { - swss::FieldValueTuple("ENUM_COUNT", std::to_string(enumCapList.count)) - }; - - SWSS_LOG_DEBUG("Sending response: count = %u", enumCapList.count); - } - - m_selectableChannel->set(sai_serialize_status(status), entry, REDIS_ASIC_STATE_COMMAND_ATTR_ENUM_VALUES_CAPABILITY_RESPONSE); - - return status; -} - -sai_status_t Syncd::processObjectTypeGetAvailabilityQuery( - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - auto& strSwitchVid = kfvKey(kco); - - sai_object_id_t switchVid; - sai_deserialize_object_id(strSwitchVid, switchVid); - - const sai_object_id_t switchRid = m_translator->translateVidToRid(switchVid); - - std::vector values = kfvFieldsValues(kco); - - // Syncd needs to pop the object type off the end of the list in order to - // retrieve the attribute list - - sai_object_type_t objectType; - sai_deserialize_object_type(fvValue(values.back()), objectType); - - values.pop_back(); - - SaiAttributeList list(objectType, values, false); - - sai_attribute_t *attr_list = list.get_attr_list(); - - uint32_t attr_count = list.get_attr_count(); - - m_translator->translateVidToRid(objectType, attr_count, attr_list); - - uint64_t count; - - sai_status_t status = m_vendorSai->objectTypeGetAvailability( - switchRid, - objectType, - attr_count, - attr_list, - &count); - - std::vector entry; - - if (status == SAI_STATUS_SUCCESS) - { - entry.push_back(swss::FieldValueTuple("OBJECT_COUNT", std::to_string(count))); - - SWSS_LOG_DEBUG("Sending response: count = %lu", count); - } - - m_selectableChannel->set(sai_serialize_status(status), entry, REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_RESPONSE); - - return status; -} - -sai_status_t Syncd::processStatsCapabilityQuery( - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - auto& strSwitchVid = kfvKey(kco); - - sai_object_id_t switchVid; - sai_deserialize_object_id(strSwitchVid, switchVid); - - sai_object_id_t switchRid = m_translator->translateVidToRid(switchVid); - - auto& values = kfvFieldsValues(kco); - - if (values.size() != 2) - { - SWSS_LOG_ERROR("Invalid input: expected 2 arguments, received %zu", values.size()); - - m_selectableChannel->set(sai_serialize_status(SAI_STATUS_INVALID_PARAMETER), {}, REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE); - - return SAI_STATUS_INVALID_PARAMETER; - } - - sai_object_type_t objectType; - sai_deserialize_object_type(fvValue(values[0]), objectType); - - uint32_t list_size = std::stoi(fvValue(values[1])); - - std::vector stat_capability_list(list_size); - - sai_stat_capability_list_t statCapList; - - statCapList.count = list_size; - statCapList.list = stat_capability_list.data(); - - sai_status_t status = m_vendorSai->queryStatsCapability(switchRid, objectType, &statCapList); - - std::vector entry; - - if (status == SAI_STATUS_SUCCESS) - { - std::vector vec_stat_enum; - std::vector vec_stat_modes; - - for (uint32_t it = 0; it < statCapList.count; it++) - { - vec_stat_enum.push_back(std::to_string(statCapList.list[it].stat_enum)); - vec_stat_modes.push_back(std::to_string(statCapList.list[it].stat_modes)); - } - - std::ostringstream join_stat_enum; - std::copy(vec_stat_enum.begin(), vec_stat_enum.end(), std::ostream_iterator(join_stat_enum, ",")); - auto strCapEnum = join_stat_enum.str(); - - std::ostringstream join_stat_modes; - std::copy(vec_stat_modes.begin(), vec_stat_modes.end(), std::ostream_iterator(join_stat_modes, ",")); - auto strCapModes = join_stat_modes.str(); - - entry = - { - swss::FieldValueTuple("STAT_ENUM", strCapEnum), - swss::FieldValueTuple("STAT_MODES", strCapModes), - swss::FieldValueTuple("STAT_COUNT", std::to_string(statCapList.count)) - }; - - SWSS_LOG_DEBUG("Sending response: stat_enums = '%s', stat_modes = '%s', count = %d", - strCapEnum.c_str(), strCapModes.c_str(), statCapList.count); - } - else if (status == SAI_STATUS_BUFFER_OVERFLOW) - { - entry = { swss::FieldValueTuple("STAT_COUNT", std::to_string(statCapList.count)) }; - - SWSS_LOG_DEBUG("Sending response: count = %u", statCapList.count); - } - - m_selectableChannel->set(sai_serialize_status(status), entry, REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE); - - return status; -} - -sai_status_t Syncd::processStatsStCapabilityQuery( - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - auto &strSwitchVid = kfvKey(kco); - - sai_object_id_t switchVid; - sai_deserialize_object_id(strSwitchVid, switchVid); - - sai_object_id_t switchRid = m_translator->translateVidToRid(switchVid); - - auto &values = kfvFieldsValues(kco); - - if (values.size() != 2) - { - SWSS_LOG_ERROR("Invalid input: expected 2 arguments, received %zu", values.size()); - - m_selectableChannel->set(sai_serialize_status(SAI_STATUS_INVALID_PARAMETER), {}, REDIS_ASIC_STATE_COMMAND_STATS_ST_CAPABILITY_RESPONSE); - - return SAI_STATUS_INVALID_PARAMETER; - } - - sai_object_type_t objectType; - sai_deserialize_object_type(fvValue(values[0]), objectType); - - uint32_t list_size = std::stoi(fvValue(values[1])); - - std::vector stat_capability_list(list_size); - - sai_stat_st_capability_list_t statCapList; - - statCapList.count = list_size; - statCapList.list = stat_capability_list.data(); - - sai_status_t status = m_vendorSai->queryStatsStCapability(switchRid, objectType, &statCapList); - - std::vector entry; - - if (status == SAI_STATUS_SUCCESS) - { - std::vector vec_stat_enum; - std::vector vec_stat_modes; - std::vector vec_minimal_polling_intervals; - - for (uint32_t it = 0; it < statCapList.count; it++) - { - vec_stat_enum.push_back(std::to_string(statCapList.list[it].capability.stat_enum)); - vec_stat_modes.push_back(std::to_string(statCapList.list[it].capability.stat_modes)); - vec_minimal_polling_intervals.push_back(std::to_string(statCapList.list[it].minimal_polling_interval)); - } - - std::ostringstream join_stat_enum; - std::copy(vec_stat_enum.begin(), vec_stat_enum.end(), std::ostream_iterator(join_stat_enum, ",")); - auto strCapEnum = join_stat_enum.str(); - - std::ostringstream join_stat_modes; - std::copy(vec_stat_modes.begin(), vec_stat_modes.end(), std::ostream_iterator(join_stat_modes, ",")); - auto strCapModes = join_stat_modes.str(); - - std::ostringstream join_minimal_polling_intervals; - std::copy(vec_minimal_polling_intervals.begin(), vec_minimal_polling_intervals.end(), std::ostream_iterator(join_minimal_polling_intervals, ",")); - auto strCapMinPollInt = join_minimal_polling_intervals.str(); - - entry = - { - swss::FieldValueTuple("STAT_ENUM", strCapEnum), - swss::FieldValueTuple("STAT_MODES", strCapModes), - swss::FieldValueTuple("MINIMAL_POLLING_INTERVALS", strCapMinPollInt), - swss::FieldValueTuple("STAT_COUNT", std::to_string(statCapList.count))}; - - SWSS_LOG_DEBUG("Sending response: stat_enums = '%s', stat_modes = '%s', minimal_polling_intervals = '%s' count = %d", - strCapEnum.c_str(), strCapModes.c_str(), strCapMinPollInt.c_str(), statCapList.count); - } - else if (status == SAI_STATUS_BUFFER_OVERFLOW) - { - entry = {swss::FieldValueTuple("STAT_COUNT", std::to_string(statCapList.count))}; - - SWSS_LOG_DEBUG("Sending response: count = %u", statCapList.count); - } - - m_selectableChannel->set(sai_serialize_status(status), entry, REDIS_ASIC_STATE_COMMAND_STATS_ST_CAPABILITY_RESPONSE); - - return status; -} - -sai_status_t Syncd::processFdbFlush( - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - auto& key = kfvKey(kco); - auto strSwitchVid = key.substr(key.find(":") + 1); - - sai_object_id_t switchVid; - sai_deserialize_object_id(strSwitchVid, switchVid); - - sai_object_id_t switchRid = m_translator->translateVidToRid(switchVid); - - auto& values = kfvFieldsValues(kco); - - for (const auto &v: values) - { - SWSS_LOG_DEBUG("attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str()); - } - - SaiAttributeList list(SAI_OBJECT_TYPE_FDB_FLUSH, values, false); - SaiAttributeList vidlist(SAI_OBJECT_TYPE_FDB_FLUSH, values, false); - - /* - * Attribute list can't be const since we will use it to translate VID to - * RID in place. - */ - - sai_attribute_t *attr_list = list.get_attr_list(); - uint32_t attr_count = list.get_attr_count(); - - m_translator->translateVidToRid(SAI_OBJECT_TYPE_FDB_FLUSH, attr_count, attr_list); - - sai_status_t status = m_vendorSai->flushFdbEntries(switchRid, attr_count, attr_list); - - m_selectableChannel->set(sai_serialize_status(status), {} , REDIS_ASIC_STATE_COMMAND_FLUSHRESPONSE); - - if (status == SAI_STATUS_SUCCESS) - { - SWSS_LOG_NOTICE("fdb flush succeeded, updating redis database"); - - // update database right after fdb flush success (not in notification) - // build artificial notification here to reuse code - - auto *md = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_FDB_FLUSH, SAI_FDB_FLUSH_ATTR_ENTRY_TYPE); - auto *dv = md ? md->defaultvalue : nullptr; - - sai_fdb_flush_entry_type_t type = dv - ? (sai_fdb_flush_entry_type_t)dv->s32 - : SAI_FDB_FLUSH_ENTRY_TYPE_DYNAMIC; - - sai_object_id_t bvId = SAI_NULL_OBJECT_ID; - sai_object_id_t bridgePortId = SAI_NULL_OBJECT_ID; - - attr_list = vidlist.get_attr_list(); - attr_count = vidlist.get_attr_count(); - - for (uint32_t i = 0; i < attr_count; i++) - { - switch (attr_list[i].id) - { - case SAI_FDB_FLUSH_ATTR_BRIDGE_PORT_ID: - bridgePortId = attr_list[i].value.oid; - break; - - case SAI_FDB_FLUSH_ATTR_BV_ID: - bvId = attr_list[i].value.oid; - break; - - case SAI_FDB_FLUSH_ATTR_ENTRY_TYPE: - type = (sai_fdb_flush_entry_type_t)attr_list[i].value.s32; - break; - - default: - SWSS_LOG_ERROR("unsupported attribute: %d, skipping", attr_list[i].id); - break; - } - } - - m_client->processFlushEvent(switchVid, bridgePortId, bvId, type); - } - - return status; -} - -sai_status_t Syncd::processClearStatsEvent( - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - const std::string &key = kfvKey(kco); - - sai_object_meta_key_t metaKey; - sai_deserialize_object_meta_key(key, metaKey); - - if (isInitViewMode() && m_createdInInitView.find(metaKey.objectkey.key.object_id) != m_createdInInitView.end()) - { - SWSS_LOG_WARN("CLEAR STATS api can't be used on %s since it's created in INIT_VIEW mode", key.c_str()); - - sai_status_t status = SAI_STATUS_INVALID_OBJECT_ID; - - m_selectableChannel->set(sai_serialize_status(status), {}, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); - - return status; - } - - if (!m_translator->tryTranslateVidToRid(metaKey)) - { - SWSS_LOG_WARN("VID to RID translation failure: %s", key.c_str()); - sai_status_t status = SAI_STATUS_INVALID_OBJECT_ID; - m_selectableChannel->set(sai_serialize_status(status), {}, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); - return status; - } - - auto info = sai_metadata_get_object_type_info(metaKey.objecttype); - - if (info->isnonobjectid) - { - SWSS_LOG_THROW("non object id not supported on clear stats: %s, FIXME", key.c_str()); - } - - std::vector counter_ids; - - for (auto&v: kfvFieldsValues(kco)) - { - int32_t val; - sai_deserialize_enum(fvField(v), info->statenum, val); - - counter_ids.push_back(val); - } - - auto status = m_vendorSai->clearStats( - metaKey.objecttype, - metaKey.objectkey.key.object_id, - (uint32_t)counter_ids.size(), - counter_ids.data()); - - m_selectableChannel->set(sai_serialize_status(status), {}, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); - - return status; -} - -sai_status_t Syncd::processGetStatsEvent( - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - const std::string &key = kfvKey(kco); - - sai_object_meta_key_t metaKey; - sai_deserialize_object_meta_key(key, metaKey); - - if (isInitViewMode() && m_createdInInitView.find(metaKey.objectkey.key.object_id) != m_createdInInitView.end()) - { - SWSS_LOG_WARN("GET STATS api can't be used on %s since it's created in INIT_VIEW mode", key.c_str()); - - sai_status_t status = SAI_STATUS_INVALID_OBJECT_ID; - - m_selectableChannel->set(sai_serialize_status(status), {}, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); - - return status; - } - - m_translator->translateVidToRid(metaKey); - - auto info = sai_metadata_get_object_type_info(metaKey.objecttype); - - if (info->isnonobjectid) - { - SWSS_LOG_THROW("non object id not supported on clear stats: %s, FIXME", key.c_str()); - } - - std::vector counter_ids; - - for (auto&v: kfvFieldsValues(kco)) - { - int32_t val; - sai_deserialize_enum(fvField(v), info->statenum, val); - - counter_ids.push_back(val); - } - - std::vector result(counter_ids.size()); - - auto status = m_vendorSai->getStats( - metaKey.objecttype, - metaKey.objectkey.key.object_id, - (uint32_t)counter_ids.size(), - counter_ids.data(), - result.data()); - - std::vector entry; - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_NOTICE("Getting stats error: %s", sai_serialize_status(status).c_str()); - } - else - { - const auto& values = kfvFieldsValues(kco); - - for (size_t i = 0; i < values.size(); i++) - { - entry.emplace_back(fvField(values[i]), std::to_string(result[i])); - } - } - - m_selectableChannel->set(sai_serialize_status(status), entry, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); - - return status; -} - -sai_status_t Syncd::processBulkQuadEvent( - _In_ sai_common_api_t api, - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - const std::string& key = kfvKey(kco); // objectType:count - - std::string strObjectType = key.substr(0, key.find(":")); - - sai_object_type_t objectType; - sai_deserialize_object_type(strObjectType, objectType); - - const std::vector &values = kfvFieldsValues(kco); - - std::vector> strAttributes; - - // field = objectId - // value = attrid=attrvalue|... - - std::vector objectIds; - - std::vector> attributes; - - for (const auto &fvt: values) - { - std::string strObjectId = fvField(fvt); - std::string joined = fvValue(fvt); - - // decode values - - auto v = swss::tokenize(joined, '|'); - - objectIds.push_back(strObjectId); - - std::vector entries; // attributes per object id - - for (size_t i = 0; i < v.size(); ++i) - { - const std::string item = v.at(i); - - auto start = item.find_first_of("="); - - auto field = item.substr(0, start); - auto value = item.substr(start + 1); - - entries.emplace_back(field, value); - } - - strAttributes.push_back(entries); - - // since now we converted this to proper list, we can extract attributes - - auto list = std::make_shared(objectType, entries, false); - - attributes.push_back(list); - } - - SWSS_LOG_INFO("bulk %s executing with %zu items", - strObjectType.c_str(), - objectIds.size()); - - if (isInitViewMode()) - { - return processBulkQuadEventInInitViewMode(objectType, objectIds, api, attributes, strAttributes); - } - - if (api != SAI_COMMON_API_BULK_GET) - { - // translate attributes for all objects - - for (auto &list: attributes) - { - sai_attribute_t *attr_list = list->get_attr_list(); - uint32_t attr_count = list->get_attr_count(); - - m_translator->translateVidToRid(objectType, attr_count, attr_list); - } - } - - auto info = sai_metadata_get_object_type_info(objectType); - - if (info->isobjectid) - { - return processBulkOid(objectType, objectIds, api, attributes, strAttributes); - } - else - { - return processBulkEntry(objectType, objectIds, api, attributes, strAttributes); - } -} - -sai_status_t Syncd::processBulkQuadEventInInitViewMode( - _In_ sai_object_type_t objectType, - _In_ const std::vector& objectIds, - _In_ sai_common_api_t api, - _In_ const std::vector>& attributes, - _In_ const std::vector>& strAttributes) -{ - SWSS_LOG_ENTER(); - - const auto objectCount = static_cast(objectIds.size()); - - std::vector statuses(objectIds.size()); - - const sai_status_t initialObjectStatus = api != SAI_COMMON_API_BULK_GET ? SAI_STATUS_SUCCESS : SAI_STATUS_NOT_EXECUTED; - statuses.assign(statuses.size(), initialObjectStatus); - - auto info = sai_metadata_get_object_type_info(objectType); - - switch (api) - { - case SAI_COMMON_API_BULK_CREATE: - case SAI_COMMON_API_BULK_REMOVE: - - if (info->isnonobjectid) - { - sendApiResponse(api, SAI_STATUS_SUCCESS, (uint32_t)statuses.size(), statuses.data()); - - syncUpdateRedisBulkQuadEvent(api, statuses, objectType, objectIds, strAttributes); - - return SAI_STATUS_SUCCESS; - } - - switch (objectType) - { - case SAI_OBJECT_TYPE_SWITCH: - case SAI_OBJECT_TYPE_PORT: - case SAI_OBJECT_TYPE_SCHEDULER_GROUP: - case SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP: - - SWSS_LOG_THROW("%s is not supported in init view mode", - sai_serialize_object_type(objectType).c_str()); - - default: - - sendApiResponse(api, SAI_STATUS_SUCCESS, (uint32_t)statuses.size(), statuses.data()); - - syncUpdateRedisBulkQuadEvent(api, statuses, objectType, objectIds, strAttributes); - - for (auto& str: objectIds) - { - sai_object_id_t objectVid; - sai_deserialize_object_id(str, objectVid); - - // in init view mode insert every created object except switch - - m_createdInInitView.insert(objectVid); - } - - return SAI_STATUS_SUCCESS; - } - - case SAI_COMMON_API_BULK_SET: - - switch (objectType) - { - case SAI_OBJECT_TYPE_SWITCH: - case SAI_OBJECT_TYPE_SCHEDULER_GROUP: - - SWSS_LOG_THROW("%s is not supported in init view mode", - sai_serialize_object_type(objectType).c_str()); - - default: - - break; - } - - sendApiResponse(api, SAI_STATUS_SUCCESS, (uint32_t)statuses.size(), statuses.data()); - - syncUpdateRedisBulkQuadEvent(api, statuses, objectType, objectIds, strAttributes); - - return SAI_STATUS_SUCCESS; - - case SAI_COMMON_API_BULK_GET: - if (info->isnonobjectid) - { - /* - * Those objects are user created, so if user created ROUTE he - * passed some attributes, there is no sense to support GET - * since user explicitly know what attributes were set, similar - * for other non object id types. - */ - - SWSS_LOG_ERROR("get is not supported on %s in init view mode", sai_serialize_object_type(objectType).c_str()); - - const sai_status_t status = SAI_STATUS_NOT_SUPPORTED; - sendBulkGetResponse(objectType, objectIds, status, attributes, statuses); - - return status; - } - else - { - for (size_t idx = 0; idx < objectCount; idx++) - { - const auto& strObjectId = objectIds[idx]; - - sai_object_id_t objectVid; - sai_deserialize_object_id(strObjectId, objectVid); - - if (isInitViewMode() && m_createdInInitView.find(objectVid) != m_createdInInitView.end()) - { - SWSS_LOG_WARN("GET api can't be used on %s (%s) since it's created in INIT_VIEW mode", - strObjectId.c_str(), - sai_serialize_object_type(objectType).c_str()); - - const sai_status_t status = SAI_STATUS_INVALID_OBJECT_ID; - sendBulkGetResponse(objectType, objectIds, status, attributes, statuses); - - return status; - } - - } - - return processBulkOid(objectType, objectIds, SAI_COMMON_API_BULK_GET, attributes, strAttributes); - } - - default: - - SWSS_LOG_THROW("common bulk api (%s) is not implemented in init view mode", - sai_serialize_common_api(api).c_str()); - } -} - -sai_status_t Syncd::processBulkCreateEntry( - _In_ sai_object_type_t objectType, - _In_ const std::vector& objectIds, - _In_ const std::vector>& attributes, - _Out_ std::vector& statuses) -{ - SWSS_LOG_ENTER(); - sai_status_t status = SAI_STATUS_SUCCESS; - - uint32_t object_count = (uint32_t) objectIds.size(); - - if (!object_count) - { - SWSS_LOG_ERROR("container with objectIds is empty in processBulkCreateEntry"); - return SAI_STATUS_FAILURE; - } - - sai_bulk_op_error_mode_t mode = SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR; - - std::vector attr_counts(object_count); - std::vector attr_lists(object_count); - - for (uint32_t idx = 0; idx < object_count; idx++) - { - attr_counts[idx] = attributes[idx]->get_attr_count(); - attr_lists[idx] = attributes[idx]->get_attr_list(); - } - - switch ((int)objectType) - { - case SAI_OBJECT_TYPE_ROUTE_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_route_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); - } - - static PerformanceIntervalTimer timer("Syncd::processBulkCreateEntry(route_entry) CREATE"); - - timer.start(); - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - - timer.stop(); - - timer.inc(object_count); - } - break; - - case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_neighbor_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].rif_id = m_translator->translateVidToRid(entries[it].rif_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_FDB_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_fdb_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].bv_id = m_translator->translateVidToRid(entries[it].bv_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_NAT_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_nat_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_INSEG_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_inseg_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_MY_SID_ENTRY: - { - std::vector entries(object_count); - - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_my_sid_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY: - { - std::vector entries(object_count); - - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_direction_lookup_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY: - { - std::vector entries(object_count); - - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_eni_ether_address_map_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_VIP_ENTRY: - { - std::vector entries(object_count); - - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_vip_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_INBOUND_ROUTING_ENTRY: - { - std::vector entries(object_count); - - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_inbound_routing_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].eni_id = m_translator->translateVidToRid(entries[it].eni_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_PA_VALIDATION_ENTRY: - { - std::vector entries(object_count); - - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_pa_validation_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].vnet_id = m_translator->translateVidToRid(entries[it].vnet_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY: - { - std::vector entries(object_count); - - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_outbound_routing_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].outbound_routing_group_id = m_translator->translateVidToRid(entries[it].outbound_routing_group_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY: - { - std::vector entries(object_count); - - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_outbound_ca_to_pa_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].dst_vnet_id = m_translator->translateVidToRid(entries[it].dst_vnet_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY: - { - std::vector entries(object_count); - - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_outbound_port_map_port_range_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].outbound_port_map_id = m_translator->translateVidToRid(entries[it].outbound_port_map_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_GLOBAL_TRUSTED_VNI_ENTRY: - { - std::vector entries(object_count); - - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_global_trusted_vni_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_ENI_TRUSTED_VNI_ENTRY: - { - std::vector entries(object_count); - - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_eni_trusted_vni_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].eni_id = m_translator->translateVidToRid(entries[it].eni_id); - } - - status = m_vendorSai->bulkCreate( - object_count, - entries.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - } - break; - - default: - return SAI_STATUS_NOT_SUPPORTED; - } - - return status; -} - -sai_status_t Syncd::processBulkRemoveEntry( - _In_ sai_object_type_t objectType, - _In_ const std::vector& objectIds, - _Out_ std::vector& statuses) -{ - SWSS_LOG_ENTER(); - - sai_status_t status = SAI_STATUS_SUCCESS; - - uint32_t object_count = (uint32_t) objectIds.size(); - - if (!object_count) - { - SWSS_LOG_ERROR("container with objectIds is empty in processBulkRemoveEntry"); - return SAI_STATUS_FAILURE; - } - - sai_bulk_op_error_mode_t mode = SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR; - - switch ((int)objectType) - { - case SAI_OBJECT_TYPE_ROUTE_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_route_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_neighbor_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].rif_id = m_translator->translateVidToRid(entries[it].rif_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_FDB_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_fdb_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].bv_id = m_translator->translateVidToRid(entries[it].bv_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_NAT_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_nat_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_MY_SID_ENTRY: - { - std::vector entries(object_count); - - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_my_sid_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_INSEG_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_inseg_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_direction_lookup_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_eni_ether_address_map_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_VIP_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_vip_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_INBOUND_ROUTING_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_inbound_routing_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].eni_id = m_translator->translateVidToRid(entries[it].eni_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_PA_VALIDATION_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_pa_validation_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].vnet_id = m_translator->translateVidToRid(entries[it].vnet_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_outbound_routing_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].outbound_routing_group_id = m_translator->translateVidToRid(entries[it].outbound_routing_group_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_outbound_ca_to_pa_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].dst_vnet_id = m_translator->translateVidToRid(entries[it].dst_vnet_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_outbound_port_map_port_range_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].outbound_port_map_id = m_translator->translateVidToRid(entries[it].outbound_port_map_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_GLOBAL_TRUSTED_VNI_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_global_trusted_vni_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_ENI_TRUSTED_VNI_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_eni_trusted_vni_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].eni_id = m_translator->translateVidToRid(entries[it].eni_id); - } - - status = m_vendorSai->bulkRemove( - object_count, - entries.data(), - mode, - statuses.data()); - - } - break; - - default: - return SAI_STATUS_NOT_SUPPORTED; - } - - return status; -} - -sai_status_t Syncd::processBulkSetEntry( - _In_ sai_object_type_t objectType, - _In_ const std::vector& objectIds, - _In_ const std::vector>& attributes, - _Out_ std::vector& statuses) -{ - SWSS_LOG_ENTER(); - - sai_status_t status = SAI_STATUS_SUCCESS; - - std::vector attr_lists; - - uint32_t object_count = (uint32_t) objectIds.size(); - - if (!object_count) - { - SWSS_LOG_ERROR("container with objectIds is empty in processBulkSetEntry"); - return SAI_STATUS_FAILURE; - } - - sai_bulk_op_error_mode_t mode = SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR; - - for (uint32_t it = 0; it < object_count; it++) - { - attr_lists.push_back(attributes[it]->get_attr_list()[0]); - } - - switch ((int)objectType) - { - case SAI_OBJECT_TYPE_ROUTE_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_route_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); - } - - status = m_vendorSai->bulkSet( - object_count, - entries.data(), - attr_lists.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_neighbor_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].rif_id = m_translator->translateVidToRid(entries[it].rif_id); - } - - status = m_vendorSai->bulkSet( - object_count, - entries.data(), - attr_lists.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_FDB_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_fdb_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].bv_id = m_translator->translateVidToRid(entries[it].bv_id); - } - - status = m_vendorSai->bulkSet( - object_count, - entries.data(), - attr_lists.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_NAT_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_nat_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); - } - - status = m_vendorSai->bulkSet( - object_count, - entries.data(), - attr_lists.data(), - mode, - statuses.data()); - - } - break; - - case SAI_OBJECT_TYPE_MY_SID_ENTRY: - { - std::vector entries(object_count); - - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_my_sid_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - entries[it].vr_id = m_translator->translateVidToRid(entries[it].vr_id); - } - - status = m_vendorSai->bulkSet( - object_count, - entries.data(), - attr_lists.data(), - mode, - statuses.data()); - } - break; - - case SAI_OBJECT_TYPE_INSEG_ENTRY: - { - std::vector entries(object_count); - for (uint32_t it = 0; it < object_count; it++) - { - sai_deserialize_inseg_entry(objectIds[it], entries[it]); - - entries[it].switch_id = m_translator->translateVidToRid(entries[it].switch_id); - } - - status = m_vendorSai->bulkSet( - object_count, - entries.data(), - attr_lists.data(), - mode, - statuses.data()); - - } - break; - - default: - return SAI_STATUS_NOT_SUPPORTED; - } - - return status; -} - -sai_status_t Syncd::processBulkEntry( - _In_ sai_object_type_t objectType, - _In_ const std::vector& objectIds, - _In_ sai_common_api_t api, - _In_ const std::vector>& attributes, - _In_ const std::vector>& strAttributes) -{ - SWSS_LOG_ENTER(); - - auto info = sai_metadata_get_object_type_info(objectType); - - if (info->isobjectid) - { - SWSS_LOG_THROW("passing oid object to bulk non object id operation"); - } - - std::vector statuses(objectIds.size()); - - sai_status_t all = SAI_STATUS_SUCCESS; - - if (m_commandLineOptions->m_enableSaiBulkSupport) - { - switch (api) - { - case SAI_COMMON_API_BULK_CREATE: - all = processBulkCreateEntry(objectType, objectIds, attributes, statuses); - break; - - case SAI_COMMON_API_BULK_REMOVE: - all = processBulkRemoveEntry(objectType, objectIds, statuses); - break; - - case SAI_COMMON_API_BULK_SET: - all = processBulkSetEntry(objectType, objectIds, attributes, statuses); - break; - - default: - SWSS_LOG_ERROR("api %s is not supported in bulk", sai_serialize_common_api(api).c_str()); - all = SAI_STATUS_NOT_SUPPORTED; - } - - if (all != SAI_STATUS_NOT_SUPPORTED && all != SAI_STATUS_NOT_IMPLEMENTED) - { - sendApiResponse(api, all, (uint32_t)objectIds.size(), statuses.data()); - syncUpdateRedisBulkQuadEvent(api, statuses, objectType, objectIds, strAttributes); - - return all; - } - } - - // vendor SAI don't bulk API yet, so execute one by one - - all = SAI_STATUS_SUCCESS; - - for (size_t idx = 0; idx < objectIds.size(); ++idx) - { - sai_object_meta_key_t metaKey; - - metaKey.objecttype = objectType; - - switch ((int)objectType) - { - case SAI_OBJECT_TYPE_ROUTE_ENTRY: - sai_deserialize_route_entry(objectIds[idx], metaKey.objectkey.key.route_entry); - break; - - case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY: - sai_deserialize_neighbor_entry(objectIds[idx], metaKey.objectkey.key.neighbor_entry); - break; - - case SAI_OBJECT_TYPE_NAT_ENTRY: - sai_deserialize_nat_entry(objectIds[idx], metaKey.objectkey.key.nat_entry); - break; - - case SAI_OBJECT_TYPE_FDB_ENTRY: - sai_deserialize_fdb_entry(objectIds[idx], metaKey.objectkey.key.fdb_entry); - break; - - case SAI_OBJECT_TYPE_INSEG_ENTRY: - sai_deserialize_inseg_entry(objectIds[idx], metaKey.objectkey.key.inseg_entry); - break; - - case SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY: - sai_deserialize_direction_lookup_entry(objectIds[idx], metaKey.objectkey.key.direction_lookup_entry); - break; - - case SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY: - sai_deserialize_eni_ether_address_map_entry(objectIds[idx], metaKey.objectkey.key.eni_ether_address_map_entry); - break; - - case SAI_OBJECT_TYPE_VIP_ENTRY: - sai_deserialize_vip_entry(objectIds[idx], metaKey.objectkey.key.vip_entry); - break; - - case SAI_OBJECT_TYPE_INBOUND_ROUTING_ENTRY: - sai_deserialize_inbound_routing_entry(objectIds[idx], metaKey.objectkey.key.inbound_routing_entry); - break; - - case SAI_OBJECT_TYPE_PA_VALIDATION_ENTRY: - sai_deserialize_pa_validation_entry(objectIds[idx], metaKey.objectkey.key.pa_validation_entry); - break; - - case SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY: - sai_deserialize_outbound_routing_entry(objectIds[idx], metaKey.objectkey.key.outbound_routing_entry); - break; - - case SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY: - sai_deserialize_outbound_ca_to_pa_entry(objectIds[idx], metaKey.objectkey.key.outbound_ca_to_pa_entry); - break; - - case SAI_OBJECT_TYPE_OUTBOUND_PORT_MAP_PORT_RANGE_ENTRY: - sai_deserialize_outbound_port_map_port_range_entry(objectIds[idx], metaKey.objectkey.key.outbound_port_map_port_range_entry); - break; - - case SAI_OBJECT_TYPE_GLOBAL_TRUSTED_VNI_ENTRY: - sai_deserialize_global_trusted_vni_entry(objectIds[idx], metaKey.objectkey.key.global_trusted_vni_entry); - break; - - case SAI_OBJECT_TYPE_ENI_TRUSTED_VNI_ENTRY: - sai_deserialize_eni_trusted_vni_entry(objectIds[idx], metaKey.objectkey.key.eni_trusted_vni_entry); - break; - - default: - SWSS_LOG_THROW("object %s not implemented, FIXME", sai_serialize_object_type(objectType).c_str()); - } - - sai_status_t status = SAI_STATUS_FAILURE; - - auto& list = attributes[idx]; - - sai_attribute_t *attr_list = list->get_attr_list(); - uint32_t attr_count = list->get_attr_count(); - - if (api == SAI_COMMON_API_BULK_CREATE) - { - if (objectType == SAI_OBJECT_TYPE_ROUTE_ENTRY) - { - static PerformanceIntervalTimer timer("Syncd::processBulkEntry::processEntry(route_entry) CREATE"); - - timer.start(); - - status = processEntry(metaKey, SAI_COMMON_API_CREATE, attr_count, attr_list); - - timer.stop(); - - timer.inc(); - } - else - { - status = processEntry(metaKey, SAI_COMMON_API_CREATE, attr_count, attr_list); - } - } - else if (api == SAI_COMMON_API_BULK_REMOVE) - { - status = processEntry(metaKey, SAI_COMMON_API_REMOVE, attr_count, attr_list); - } - else if (api == SAI_COMMON_API_BULK_SET) - { - status = processEntry(metaKey, SAI_COMMON_API_SET, attr_count, attr_list); - } - else - { - SWSS_LOG_THROW("api %d is not supported in bulk mode", api); - } - - if (api != SAI_COMMON_API_BULK_GET && status != SAI_STATUS_SUCCESS) - { - if (!m_enableSyncMode) - { - SWSS_LOG_THROW("operation %s for %s failed in async mode!", - sai_serialize_common_api(api).c_str(), - sai_serialize_object_type(objectType).c_str()); - } - - all = SAI_STATUS_FAILURE; // all can be success if all has been success - } - - statuses[idx] = status; - } - - sendApiResponse(api, all, (uint32_t)objectIds.size(), statuses.data()); - - syncUpdateRedisBulkQuadEvent(api, statuses, objectType, objectIds, strAttributes); - - return all; -} - -sai_status_t Syncd::processEntry( - _In_ sai_object_meta_key_t metaKey, - _In_ sai_common_api_t api, - _In_ uint32_t attr_count, - _In_ sai_attribute_t *attr_list) -{ - SWSS_LOG_ENTER(); - - m_translator->translateVidToRid(metaKey); - - switch (api) - { - case SAI_COMMON_API_CREATE: - return m_vendorSai->create(metaKey, SAI_NULL_OBJECT_ID, attr_count, attr_list); - - case SAI_COMMON_API_REMOVE: - return m_vendorSai->remove(metaKey); - - case SAI_COMMON_API_SET: - return m_vendorSai->set(metaKey, attr_list); - - case SAI_COMMON_API_GET: - return m_vendorSai->get(metaKey, attr_count, attr_list); - - default: - - SWSS_LOG_THROW("api %s not supported", sai_serialize_common_api(api).c_str()); - } -} - -sai_status_t Syncd::processBulkOidCreate( - _In_ sai_object_type_t objectType, - _In_ sai_bulk_op_error_mode_t mode, - _In_ const std::vector& objectIds, - _In_ const std::vector>& attributes, - _Out_ std::vector& statuses) -{ - SWSS_LOG_ENTER(); - - sai_status_t status = SAI_STATUS_SUCCESS; - uint32_t object_count = (uint32_t)objectIds.size(); - - if (!object_count) - { - SWSS_LOG_ERROR("container with objectIds is empty in processBulkOidCreate"); - return SAI_STATUS_FAILURE; - } - - std::vector objectVids(object_count); - - std::vector attr_counts(object_count); - std::vector attr_lists(object_count); - - for (size_t idx = 0; idx < object_count; idx++) - { - sai_deserialize_object_id(objectIds[idx], objectVids[idx]); - - attr_counts[idx] = attributes[idx]->get_attr_count(); - attr_lists[idx] = attributes[idx]->get_attr_list(); - } - - sai_object_id_t switchRid = SAI_NULL_OBJECT_ID; - - sai_object_id_t switchVid = VidManager::switchIdQuery(objectVids.front()); - switchRid = m_translator->translateVidToRid(switchVid); - - - std::vector objectRids(object_count); - - status = m_vendorSai->bulkCreate( - objectType, - switchRid, - object_count, - attr_counts.data(), - attr_lists.data(), - mode, - objectRids.data(), - statuses.data()); - - if (status == SAI_STATUS_NOT_IMPLEMENTED || status == SAI_STATUS_NOT_SUPPORTED) - { - SWSS_LOG_WARN("bulkCreate api is not implemented or not supported, object_type = %s", - sai_serialize_object_type(objectType).c_str()); - return status; - } - - /* - * Create vectors for successfully created objects only, since objectRids/Vids - * contain both successful and failed entries. Only store successful mappings - * in Redis. - */ - std::vector createdRids, createdVids; - createdRids.reserve(object_count); - createdVids.reserve(object_count); - - for (size_t idx = 0; idx < object_count; idx++) - { - if (statuses[idx] == SAI_STATUS_SUCCESS) - { - createdRids.push_back(objectRids[idx]); - createdVids.push_back(objectVids[idx]); - } - } - - m_translator->insertRidsAndVids(createdRids.size(), createdRids.data(), createdVids.data()); - - if (objectType == SAI_OBJECT_TYPE_PORT) - { - m_switches.at(switchVid)->onPostPortsCreate(createdRids.size(), createdRids.data()); - } - - return status; -} - -sai_status_t Syncd::processBulkOidSet( - _In_ sai_object_type_t objectType, - _In_ sai_bulk_op_error_mode_t mode, - _In_ const std::vector& objectIds, - _In_ const std::vector>& attributes, - _Out_ std::vector& statuses) -{ - SWSS_LOG_ENTER(); - - sai_status_t status = SAI_STATUS_SUCCESS; - uint32_t object_count = static_cast(objectIds.size()); - - if (!object_count) - { - SWSS_LOG_ERROR("container with objectIds is empty in processBulkOidSet"); - return SAI_STATUS_FAILURE; - } - - std::vector objectVids(object_count); - std::vector objectRids(object_count); - - std::vector attr_list(object_count); - - for (size_t idx = 0; idx < object_count; idx++) - { - sai_deserialize_object_id(objectIds[idx], objectVids[idx]); - objectRids[idx] = m_translator->translateVidToRid(objectVids[idx]); - - const auto attr_count = attributes[idx]->get_attr_count(); - if (attr_count != 1) - { - SWSS_LOG_THROW("bulkSet api requires one attribute per object"); - } - - attr_list[idx] = *attributes[idx]->get_attr_list(); - } - - status = m_vendorSai->bulkSet( - objectType, - object_count, - objectRids.data(), - attr_list.data(), - mode, - statuses.data()); - - if (status == SAI_STATUS_NOT_IMPLEMENTED || status == SAI_STATUS_NOT_SUPPORTED) - { - SWSS_LOG_WARN("bulkSet api is not implemented or not supported, object_type = %s", - sai_serialize_object_type(objectType).c_str()); - } - - return status; -} - -sai_status_t Syncd::processBulkOidGet( - _In_ sai_object_type_t objectType, - _In_ sai_bulk_op_error_mode_t mode, - _In_ const std::vector& objectIds, - _In_ const std::vector>& attributes, - _Out_ std::vector& statuses) -{ - SWSS_LOG_ENTER(); - - const auto object_count = static_cast(objectIds.size()); - - if (!object_count) - { - SWSS_LOG_ERROR("container with objectIds is empty in processBulkOidGet"); - return SAI_STATUS_FAILURE; - } - - std::vector objectVids(object_count); - std::vector objectRids(object_count); - - std::vector attr_counts(object_count); - std::vector attr_lists(object_count); - - for (size_t idx = 0; idx < object_count; idx++) - { - sai_deserialize_object_id(objectIds[idx], objectVids[idx]); - objectRids[idx] = m_translator->translateVidToRid(objectVids[idx]); - - attr_counts[idx] = attributes[idx]->get_attr_count(); - attr_lists[idx] = attributes[idx]->get_attr_list(); - } - - const auto status = m_vendorSai->bulkGet(objectType, - object_count, - objectRids.data(), - attr_counts.data(), - attr_lists.data(), - mode, - statuses.data()); - - if (status == SAI_STATUS_NOT_IMPLEMENTED || status == SAI_STATUS_NOT_SUPPORTED) - { - SWSS_LOG_WARN("bulkGet api is not implemented or not supported, object_type = %s", - sai_serialize_object_type(objectType).c_str()); - return status; - } - - return status; -} - -sai_status_t Syncd::processBulkOidRemove( - _In_ sai_object_type_t objectType, - _In_ sai_bulk_op_error_mode_t mode, - _In_ const std::vector& objectIds, - _Out_ std::vector& statuses) -{ - SWSS_LOG_ENTER(); - - sai_status_t status = SAI_STATUS_SUCCESS; - uint32_t object_count = (uint32_t)objectIds.size(); - - if (!object_count) - { - SWSS_LOG_ERROR("container with objectIds is empty in processBulkOidRemove"); - return SAI_STATUS_FAILURE; - } - - std::vector objectVids(object_count); - std::vector objectRids(object_count); - - for (size_t idx = 0; idx < object_count; idx++) - { - sai_deserialize_object_id(objectIds[idx], objectVids[idx]); - objectRids[idx] = m_translator->translateVidToRid(objectVids[idx]); - - if (objectType == SAI_OBJECT_TYPE_PORT) - { - sai_object_id_t switchVid = VidManager::switchIdQuery(objectVids[idx]); - m_switches.at(switchVid)->collectPortRelatedObjects(objectRids[idx]); - } - } - - status = m_vendorSai->bulkRemove( - objectType, - (uint32_t)object_count, - objectRids.data(), - mode, - statuses.data()); - - if (status == SAI_STATUS_NOT_IMPLEMENTED || status == SAI_STATUS_NOT_SUPPORTED) - { - SWSS_LOG_WARN("bulkRemove api is not implemented or not supported, object_type = %s", - sai_serialize_object_type(objectType).c_str()); - return status; - } - - /* - * remove all related objects from REDIS DB and also from existing - * object references since at this point they are no longer valid - */ - sai_object_id_t switchVid; - for (size_t idx = 0; idx < object_count; idx++) - { - if (statuses[idx] == SAI_STATUS_SUCCESS) - { - m_translator->eraseRidAndVid(objectRids[idx], objectVids[idx]); - - switchVid = VidManager::switchIdQuery(objectVids[idx]); - - if (m_switches.at(switchVid)->isDiscoveredRid(objectRids[idx])) - { - m_switches.at(switchVid)->removeExistingObjectReference(objectRids[idx]); - } - - if (objectType == SAI_OBJECT_TYPE_PORT) - { - m_switches.at(switchVid)->postPortRemove(objectRids[idx]); - } - } - } - - return status; -} - -sai_status_t Syncd::processBulkOid( - _In_ sai_object_type_t objectType, - _In_ const std::vector& objectIds, - _In_ sai_common_api_t api, - _In_ const std::vector>& attributes, - _In_ const std::vector>& strAttributes) -{ - SWSS_LOG_ENTER(); - - auto info = sai_metadata_get_object_type_info(objectType); - - if (info->isnonobjectid) - { - SWSS_LOG_THROW("passing non object id to bulk oid object operation"); - } - - std::vector statuses(objectIds.size()); - - sai_status_t all = SAI_STATUS_SUCCESS; - - if (m_commandLineOptions->m_enableSaiBulkSupport) - { - sai_bulk_op_error_mode_t mode = SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR; - - switch (api) - { - case SAI_COMMON_API_BULK_CREATE: - all = processBulkOidCreate(objectType, mode, objectIds, attributes, statuses); - break; - - case SAI_COMMON_API_BULK_SET: - all = processBulkOidSet(objectType, mode, objectIds, attributes, statuses); - break; - - case SAI_COMMON_API_BULK_GET: - all = processBulkOidGet(objectType, mode, objectIds, attributes, statuses); - break; - - case SAI_COMMON_API_BULK_REMOVE: - all = processBulkOidRemove(objectType, mode, objectIds, statuses); - break; - - default: - all = SAI_STATUS_NOT_SUPPORTED; - SWSS_LOG_ERROR("api %s is not supported in bulk mode", sai_serialize_common_api(api).c_str()); - } - - if (all != SAI_STATUS_NOT_SUPPORTED && all != SAI_STATUS_NOT_IMPLEMENTED) - { - switch (api) - { - case SAI_COMMON_API_BULK_GET: - sendBulkGetResponse(objectType, objectIds, all, attributes, statuses); - break; - default: - sendApiResponse(api, all, (uint32_t)objectIds.size(), statuses.data()); - break; - } - - syncUpdateRedisBulkQuadEvent(api, statuses, objectType, objectIds, strAttributes); - return all; - } - } - - // vendor SAI don't bulk API yet, so execute one by one - - all = SAI_STATUS_SUCCESS; - - for (size_t idx = 0; idx < objectIds.size(); ++idx) - { - sai_status_t status = SAI_STATUS_FAILURE; - - auto& list = attributes[idx]; - - sai_attribute_t *attr_list = list->get_attr_list(); - uint32_t attr_count = list->get_attr_count(); - - if (api == SAI_COMMON_API_BULK_CREATE) - { - status = processOid(objectType, objectIds[idx], SAI_COMMON_API_CREATE, attr_count, attr_list); - } - else if (api == SAI_COMMON_API_BULK_REMOVE) - { - status = processOid(objectType, objectIds[idx], SAI_COMMON_API_REMOVE, attr_count, attr_list); - } - else if (api == SAI_COMMON_API_BULK_SET) - { - status = processOid(objectType, objectIds[idx], SAI_COMMON_API_SET, attr_count, attr_list); - } - else if (api == SAI_COMMON_API_BULK_GET) - { - status = processOid(objectType, objectIds[idx], SAI_COMMON_API_GET, attr_count, attr_list); - } - else - { - SWSS_LOG_THROW("api %s is not supported in bulk mode", - sai_serialize_common_api(api).c_str()); - } - - if (status != SAI_STATUS_SUCCESS) - { - if (!m_enableSyncMode) - { - SWSS_LOG_THROW("operation %s for %s failed in async mode!", - sai_serialize_common_api(api).c_str(), - sai_serialize_object_type(objectType).c_str()); - } - - all = SAI_STATUS_FAILURE; // all can be success if all has been success - } - - statuses[idx] = status; - } - - switch (api) - { - case SAI_COMMON_API_BULK_GET: - sendBulkGetResponse(objectType, objectIds, all, attributes, statuses); - break; - default: - sendApiResponse(api, all, (uint32_t)objectIds.size(), statuses.data()); - break; - } - - syncUpdateRedisBulkQuadEvent(api, statuses, objectType, objectIds, strAttributes); - - return all; -} - -sai_status_t Syncd::processQuadEventInInitViewMode( - _In_ sai_object_type_t objectType, - _In_ const std::string& strObjectId, - _In_ sai_common_api_t api, - _In_ uint32_t attr_count, - _In_ sai_attribute_t *attr_list) -{ - SWSS_LOG_ENTER(); - - /* - * Since attributes are not checked, it may happen that user will send some - * invalid VID in object id/list in attribute, metadata should handle that, - * but if that happen, this id will be treated as "new" object instead of - * existing one. - */ - - switch (api) - { - case SAI_COMMON_API_CREATE: - return processQuadInInitViewModeCreate(objectType, strObjectId, attr_count, attr_list); - - case SAI_COMMON_API_REMOVE: - return processQuadInInitViewModeRemove(objectType, strObjectId); - - case SAI_COMMON_API_SET: - return processQuadInInitViewModeSet(objectType, strObjectId, attr_list); - - case SAI_COMMON_API_GET: - return processQuadInInitViewModeGet(objectType, strObjectId, attr_count, attr_list); - - default: - - SWSS_LOG_THROW("common api (%s) is not implemented in init view mode", sai_serialize_common_api(api).c_str()); - } -} - -sai_status_t Syncd::processQuadInInitViewModeCreate( - _In_ sai_object_type_t objectType, - _In_ const std::string& strObjectId, - _In_ uint32_t attr_count, - _In_ sai_attribute_t *attr_list) -{ - SWSS_LOG_ENTER(); - - if (objectType == SAI_OBJECT_TYPE_PORT) - { - /* - * Reason for this is that if user will create port, new port is not - * actually created so when for example querying new queues for new - * created port, there are not there, since no actual port create was - * issued on the ASIC. - */ - - SWSS_LOG_THROW("port object can't be created in init view mode"); - } - - auto info = sai_metadata_get_object_type_info(objectType); - - // we assume create of those non object id object types will succeed - - if (info->isobjectid) - { - sai_object_id_t objectVid; - sai_deserialize_object_id(strObjectId, objectVid); - - /* - * Object ID here is actual VID returned from redis during - * creation this is floating VID in init view mode. - */ - - SWSS_LOG_DEBUG("generic create (init view) for %s, floating VID: %s", - sai_serialize_object_type(objectType).c_str(), - sai_serialize_object_id(objectVid).c_str()); - - if (objectType == SAI_OBJECT_TYPE_SWITCH) - { - onSwitchCreateInInitViewMode(objectVid, attr_count, attr_list); - } - else - { - // in init view mode insert every created object except switch - - m_createdInInitView.insert(objectVid); - } - } - - sendApiResponse(SAI_COMMON_API_CREATE, SAI_STATUS_SUCCESS); - - return SAI_STATUS_SUCCESS; -} - -sai_status_t Syncd::processQuadInInitViewModeRemove( - _In_ sai_object_type_t objectType, - _In_ const std::string& strObjectId) -{ - SWSS_LOG_ENTER(); - - if (objectType == SAI_OBJECT_TYPE_PORT) - { - /* - * Reason for this is that if user will remove port, actual resources - * for it won't be released, lanes would be still occupied and there is - * extra logic required in post port remove which clears OIDs - * (ipgs,queues,SGs) from redis db that are automatically removed by - * vendor SAI, and comparison logic don't support that. - */ - - SWSS_LOG_THROW("port object (%s) can't be removed in init view mode", strObjectId.c_str()); - } - - if (objectType == SAI_OBJECT_TYPE_SWITCH) - { - /* - * NOTE: Special care needs to be taken to clear all this switch id's - * from all db's currently we skip this since we assume that orchagent - * will not be removing switches, just creating. But it may happen - * when asic will fail etc. - * - * To support multiple switches this case must be refactored. - */ - - SWSS_LOG_THROW("remove switch (%s) is not supported in init view mode yet! FIXME", strObjectId.c_str()); - } - - // NOTE: we should also prevent removing some other non removable objects - - auto info = sai_metadata_get_object_type_info(objectType); - - if (info->isobjectid) - { - /* - * If object is existing object (like bridge port, vlan member) user - * may want to remove them, but this is temporary view, and when we - * receive apply view, we will populate existing objects to temporary - * view (since not all of them user may query) and this will produce - * conflict, since some of those objects user could explicitly remove. - * So to solve that we need to have a list of removed objects, and then - * only populate objects which not exist on removed list. - */ - - sai_object_id_t objectVid; - sai_deserialize_object_id(strObjectId, objectVid); - - // this set may contain removed objects from multiple switches - - m_initViewRemovedVidSet.insert(objectVid); - } - - sendApiResponse(SAI_COMMON_API_REMOVE, SAI_STATUS_SUCCESS); - - return SAI_STATUS_SUCCESS; -} - -sai_status_t Syncd::processQuadInInitViewModeSet( - _In_ sai_object_type_t objectType, - _In_ const std::string& strObjectId, - _In_ sai_attribute_t *attr) -{ - SWSS_LOG_ENTER(); - - // we support SET api on all objects in init view mode - - sendApiResponse(SAI_COMMON_API_SET, SAI_STATUS_SUCCESS); - - return SAI_STATUS_SUCCESS; -} - -sai_status_t Syncd::processQuadInInitViewModeGet( - _In_ sai_object_type_t objectType, - _In_ const std::string& strObjectId, - _In_ uint32_t attr_count, - _In_ sai_attribute_t *attr_list) -{ - SWSS_LOG_ENTER(); - - sai_status_t status; - - auto info = sai_metadata_get_object_type_info(objectType); - - sai_object_id_t switchVid = SAI_NULL_OBJECT_ID; - - if (info->isnonobjectid) - { - /* - * Those objects are user created, so if user created ROUTE he - * passed some attributes, there is no sense to support GET - * since user explicitly know what attributes were set, similar - * for other non object id types. - */ - - SWSS_LOG_ERROR("get is not supported on %s in init view mode", sai_serialize_object_type(objectType).c_str()); - - status = SAI_STATUS_NOT_SUPPORTED; - } - else - { - sai_object_id_t objectVid; - sai_deserialize_object_id(strObjectId, objectVid); - - if (isInitViewMode() && m_createdInInitView.find(objectVid) != m_createdInInitView.end()) - { - SWSS_LOG_WARN("GET api can't be used on %s (%s) since it's created in INIT_VIEW mode", - strObjectId.c_str(), - sai_serialize_object_type(objectType).c_str()); - - status = SAI_STATUS_INVALID_OBJECT_ID; - - sendGetResponse(objectType, strObjectId, switchVid, status, attr_count, attr_list); - - return status; - } - - switchVid = VidManager::switchIdQuery(objectVid); - - SWSS_LOG_DEBUG("generic get (init view) for object type %s:%s", - sai_serialize_object_type(objectType).c_str(), - strObjectId.c_str()); - - /* - * Object must exists, we can't call GET on created object - * in init view mode, get here can be called on existing - * objects like default trap group to get some vendor - * specific values. - * - * Exception here is switch, since all switches must be - * created, when user will create switch on init view mode, - * switch will be matched with existing switch, or it will - * be explicitly created so user can query it properties. - * - * Translate vid to rid will make sure that object exist - * and it have RID defined, so we can query it. - */ - - sai_object_id_t rid = m_translator->translateVidToRid(objectVid); - - sai_object_meta_key_t metaKey; - - metaKey.objecttype = objectType; - metaKey.objectkey.key.object_id = rid; - - status = m_vendorSai->get(metaKey, attr_count, attr_list); - } - - /* - * We are in init view mode, but ether switch already existed or first - * command was creating switch and user created switch. - * - * We could change that later on, depends on object type we can extract - * switch id, we could also have this method inside metadata to get meta - * key. - */ - - sendGetResponse(objectType, strObjectId, switchVid, status, attr_count, attr_list); - - return status; -} - -void Syncd::sendApiResponse( - _In_ sai_common_api_t api, - _In_ sai_status_t status, - _In_ uint32_t object_count, - _In_ sai_status_t* object_statuses) -{ - SWSS_LOG_ENTER(); - - /* - * By default synchronous mode is disabled and can be enabled by command - * line on syncd start. This will also require to enable synchronous mode - * in OA/sairedis because same GET RESPONSE channel is used to generate - * response for sairedis quad API. - */ - - if (!m_enableSyncMode) - { - return; - } - - switch (api) - { - case SAI_COMMON_API_CREATE: - case SAI_COMMON_API_REMOVE: - case SAI_COMMON_API_SET: - case SAI_COMMON_API_BULK_CREATE: - case SAI_COMMON_API_BULK_REMOVE: - case SAI_COMMON_API_BULK_SET: - break; - - default: - SWSS_LOG_THROW("api %s not supported by this function", - sai_serialize_common_api(api).c_str()); - } - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("api %s failed in syncd mode: %s", - sai_serialize_common_api(api).c_str(), - sai_serialize_status(status).c_str()); - } - - std::vector entry; - - for (uint32_t idx = 0; idx < object_count; idx++) - { - swss::FieldValueTuple fvt(sai_serialize_status(object_statuses[idx]), ""); - - entry.push_back(fvt); - } - - std::string strStatus = sai_serialize_status(status); - - SWSS_LOG_INFO("sending response for %s api with status: %s", - sai_serialize_common_api(api).c_str(), - strStatus.c_str()); - - m_selectableChannel->set(strStatus, entry, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); - - SWSS_LOG_INFO("response for %s api was send", - sai_serialize_common_api(api).c_str()); -} - -void Syncd::processFlexCounterGroupEvent( // TODO must be moved to go via ASIC channel queue - _In_ swss::ConsumerTable& consumer) -{ - SWSS_LOG_ENTER(); - - std::lock_guard lock(m_mutex); - - swss::KeyOpFieldsValuesTuple kco; - - consumer.pop(kco); - - auto& groupName = kfvKey(kco); - auto& op = kfvOp(kco); - auto& values = kfvFieldsValues(kco); - - WatchdogScope ws(m_timerWatchdog, op + ":" + groupName, &kco); - - processFlexCounterGroupEvent(groupName, op, values, false); -} - -sai_status_t Syncd::processFlexCounterGroupEvent( - _In_ const std::string &groupName, - _In_ const std::string &op, - _In_ const std::vector &values, - _In_ bool fromAsicChannel) -{ - SWSS_LOG_ENTER(); - - if (op == SET_COMMAND) - { - m_manager->addCounterPlugin(groupName, values); - if (fromAsicChannel) - { - m_flexCounterGroupTable->set(groupName, values); - } - } - else if (op == DEL_COMMAND) - { - if (fromAsicChannel) - { - m_flexCounterGroupTable->del(groupName); - } - m_manager->removeCounterPlugins(groupName); - } - else - { - SWSS_LOG_ERROR("unknown command: %s", op.c_str()); - } - - if (fromAsicChannel) - { - sendApiResponse(SAI_COMMON_API_SET, SAI_STATUS_SUCCESS); - } - - return SAI_STATUS_SUCCESS; -} - -void Syncd::processFlexCounterEvent( // TODO must be moved to go via ASIC channel queue - _In_ swss::ConsumerTable& consumer) -{ - SWSS_LOG_ENTER(); - - std::lock_guard lock(m_mutex); - - swss::KeyOpFieldsValuesTuple kco; - - consumer.pop(kco); - - auto& key = kfvKey(kco); - auto& op = kfvOp(kco); - auto& values = kfvFieldsValues(kco); - - WatchdogScope ws(m_timerWatchdog, op + ":" + key, &kco); - - processFlexCounterEvent(key, op, values, false); -} - -sai_status_t Syncd::processFlexCounterEvent( - _In_ const std::string &key, - _In_ const std::string &op, - _In_ const std::vector &values, - _In_ bool fromAsicChannel) -{ - SWSS_LOG_ENTER(); - - auto delimiter = key.find_first_of(":"); - - if (delimiter == std::string::npos) - { - SWSS_LOG_ERROR("Failed to parse the key %s", key.c_str()); - - if (fromAsicChannel) - { - sendApiResponse(SAI_COMMON_API_SET, SAI_STATUS_FAILURE); - } - - return SAI_STATUS_FAILURE; // if key is invalid there is no need to process this event again - } - - auto groupName = key.substr(0, delimiter); - auto strVids = key.substr(delimiter + 1); - auto vidStringVector = swss::tokenize(strVids, ','); - - if (fromAsicChannel && op == SET_COMMAND && (!vidStringVector.empty())) - { - std::vector vids; - std::vector rids; - std::vector keys; - - vids.reserve(vidStringVector.size()); - rids.reserve(vidStringVector.size()); - keys.reserve(vidStringVector.size()); - - for (auto &strVid: vidStringVector) - { - sai_object_id_t vid, rid; - sai_deserialize_object_id(strVid, vid); - vids.emplace_back(vid); - - if (!m_translator->tryTranslateVidToRid(vid, rid)) - { - SWSS_LOG_ERROR("port VID %s, was not found (probably port was removed/splitted) and will remove from counters now", - sai_serialize_object_id(vid).c_str()); - } - - rids.emplace_back(rid); - keys.emplace_back(groupName + ":" + strVid); - } - - m_manager->bulkAddCounter(vids, rids, groupName, values); - - for (auto &singleKey: keys) - { - m_flexCounterTable->set(singleKey, values); - } - - if (fromAsicChannel) - { - sendApiResponse(SAI_COMMON_API_SET, SAI_STATUS_SUCCESS); - } - - return SAI_STATUS_SUCCESS; - } - - for(auto &strVid : vidStringVector) - { - auto effective_op = op; - auto singleKey = groupName + ":" + strVid; - - sai_object_id_t vid; - sai_deserialize_object_id(strVid, vid); - - sai_object_id_t rid; - - if (!m_translator->tryTranslateVidToRid(vid, rid)) - { - if (fromAsicChannel) - { - SWSS_LOG_ERROR("port VID %s, was not found (probably port was removed/splitted) and will remove from counters now", - sai_serialize_object_id(vid).c_str()); - } - else - { - SWSS_LOG_WARN("port VID %s, was not found (probably port was removed/splitted) and will remove from counters now", - sai_serialize_object_id(vid).c_str()); - } - effective_op = DEL_COMMAND; - } - - if (effective_op == SET_COMMAND) - { - m_manager->addCounter(vid, rid, groupName, values); - if (fromAsicChannel) - { - m_flexCounterTable->set(singleKey, values); - } - } - else if (effective_op == DEL_COMMAND) - { - if (fromAsicChannel) - { - m_flexCounterTable->del(singleKey); - } - m_manager->removeCounter(vid, groupName); - } - else - { - SWSS_LOG_ERROR("unknown command: %s", op.c_str()); - } - } - - if (fromAsicChannel) - { - sendApiResponse(SAI_COMMON_API_SET, SAI_STATUS_SUCCESS); - } - - return SAI_STATUS_SUCCESS; -} - -void Syncd::syncUpdateRedisQuadEvent( - _In_ sai_status_t status, - _In_ sai_common_api_t api, - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - if (!m_enableSyncMode) - { - return; - } - - if (status != SAI_STATUS_SUCCESS) - { - return; - } - - // When in synchronous mode, we need to modify redis database when status - // is success, since consumer table on synchronous mode is not making redis - // changes and we only want to apply changes when api succeeded. This - // applies to init view mode and apply view mode. - - const std::string& key = kfvKey(kco); - - auto& values = kfvFieldsValues(kco); - - sai_object_meta_key_t metaKey; - sai_deserialize_object_meta_key(key, metaKey); - - const bool initView = isInitViewMode(); - - static PerformanceIntervalTimer timer("Syncd::syncUpdateRedisQuadEvent"); - - timer.start(); - - switch (api) - { - case SAI_COMMON_API_CREATE: - - { - if (initView) - m_client->createTempAsicObject(metaKey, values); - else - m_client->createAsicObject(metaKey, values); - - break; - } - - case SAI_COMMON_API_REMOVE: - - { - if (initView) - m_client->removeTempAsicObject(metaKey); - else - m_client->removeAsicObject(metaKey); - - break; - } - - case SAI_COMMON_API_SET: - - { - auto& first = values.at(0); - - auto& attr = fvField(first); - auto& value = fvValue(first); - - if (initView) - m_client->setTempAsicObject(metaKey, attr, value); - else - m_client->setAsicObject(metaKey, attr, value); - - break; - } - - case SAI_COMMON_API_GET: - break; // ignore get since get is not modifying db - - default: - - SWSS_LOG_THROW("api %d is not supported", api); - } - - timer.stop(); - - timer.inc(); -} - -void Syncd::syncUpdateRedisBulkQuadEvent( - _In_ sai_common_api_t api, - _In_ const std::vector& statuses, - _In_ sai_object_type_t objectType, - _In_ const std::vector& objectIds, - _In_ const std::vector>& strAttributes) -{ - SWSS_LOG_ENTER(); - - if (!m_enableSyncMode) - { - return; - } - - // When in synchronous mode, we need to modify redis database when status - // is success, since consumer table on synchronous mode is not making redis - // changes and we only want to apply changes when api succeeded. This - // applies to init view mode and apply view mode. - - static PerformanceIntervalTimer timer("Syncd::syncUpdateRedisBulkQuadEvent"); - - timer.start(); - - const std::string strObjectType = sai_serialize_object_type(objectType); - - std::unordered_map> multiHash; - - std::vector keys; - - for (size_t idx = 0; idx < statuses.size(); idx++) - { - sai_status_t status = statuses[idx]; - - if (status != SAI_STATUS_SUCCESS) - { - // in case of failure, don't modify database - continue; - } - - auto key = strObjectType + ":" + objectIds.at(idx); - - keys.push_back(key); - - if (api == SAI_COMMON_API_BULK_SET) - { - // in case of bulk set operation, it can happen that multiple - // attributes will be set for the same key, then when we want to - // push them to redis database, we need to combine all attributes - // to a single vector of attributes - - multiHash[key].push_back(strAttributes.at(idx).at(0)); - } - else - { - multiHash[key] = strAttributes.at(idx); - } - } - - const bool initView = isInitViewMode(); - - switch (api) - { - case SAI_COMMON_API_BULK_CREATE: - - { - if (initView) - m_client->createTempAsicObjects(multiHash); - else - m_client->createAsicObjects(multiHash); - - break; - } - - case SAI_COMMON_API_BULK_REMOVE: - - { - if (initView) - m_client->removeTempAsicObjects(keys); - else - m_client->removeAsicObjects(keys); - - break; - } - - case SAI_COMMON_API_BULK_SET: - - { - // SET is the same as create - if (initView) - m_client->createTempAsicObjects(multiHash); - else - m_client->createAsicObjects(multiHash); - - break; - } - - case SAI_COMMON_API_BULK_GET: - break; // ignore get since get is not modifying db - - default: - - SWSS_LOG_THROW("api %d is not supported", api); - } - - timer.stop(); - - timer.inc(statuses.size()); -} - -sai_status_t Syncd::processQuadEvent( - _In_ sai_common_api_t api, - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - const std::string& key = kfvKey(kco); - const std::string& op = kfvOp(kco); - - const std::string& strObjectId = key.substr(key.find(":") + 1); - - sai_object_meta_key_t metaKey; - sai_deserialize_object_meta_key(key, metaKey); - - if (!sai_metadata_is_object_type_valid(metaKey.objecttype)) - { - SWSS_LOG_THROW("invalid object type %s", key.c_str()); - } - - auto& values = kfvFieldsValues(kco); - - for (auto& v: values) - { - SWSS_LOG_DEBUG("attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str()); - } - - SaiAttributeList list(metaKey.objecttype, values, false); - - /* - * Attribute list can't be const since we will use it to translate VID to - * RID in place. - */ - - sai_attribute_t *attr_list = list.get_attr_list(); - uint32_t attr_count = list.get_attr_count(); - - /* - * NOTE: This check pointers must be executed before init view mode, since - * this methods replaces pointers from orchagent memory space to syncd - * memory space. - */ - - if (metaKey.objecttype == SAI_OBJECT_TYPE_SWITCH && (api == SAI_COMMON_API_CREATE || api == SAI_COMMON_API_SET)) - { - /* - * We don't need to clear those pointers on switch remove (even last), - * since those pointers will reside inside attributes, also sairedis - * will internally check whether pointer is null or not, so we here - * will receive all notifications, but redis only those that were set. - * - * TODO: must be done per switch, and switch may not exists yet - */ - - m_handler->updateNotificationsPointers(attr_count, attr_list); - } - - if (isInitViewMode()) - { - sai_status_t status = processQuadEventInInitViewMode(metaKey.objecttype, strObjectId, api, attr_count, attr_list); - - syncUpdateRedisQuadEvent(status, api, kco); - - return status; - } - - if (api != SAI_COMMON_API_GET) - { - /* - * NOTE: we can also call translate on get, if sairedis will clean - * buffer so then all OIDs will be NULL, and translation will also - * convert them to NULL. - */ - - SWSS_LOG_DEBUG("translating VID to RIDs on all attributes"); - - m_translator->translateVidToRid(metaKey.objecttype, attr_count, attr_list); - } - - auto info = sai_metadata_get_object_type_info(metaKey.objecttype); - - sai_status_t status; - - if (info->isnonobjectid) - { - if (info->objecttype == SAI_OBJECT_TYPE_ROUTE_ENTRY) - { - static PerformanceIntervalTimer timer("Syncd::processQuadEvent::processEntry(route_entry)"); - - timer.start(); - - status = processEntry(metaKey, api, attr_count, attr_list); - - timer.stop(); - - timer.inc(); - } - else - { - status = processEntry(metaKey, api, attr_count, attr_list); - } - } - else - { - status = processOid(metaKey.objecttype, strObjectId, api, attr_count, attr_list); - } - - if (api == SAI_COMMON_API_GET) - { - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_INFO("get API for key: %s op: %s returned status: %s", - key.c_str(), - op.c_str(), - sai_serialize_status(status).c_str()); - } - - // extract switch VID from any object type - - sai_object_id_t switchVid = VidManager::switchIdQuery(metaKey.objectkey.key.object_id); - - sendGetResponse(metaKey.objecttype, strObjectId, switchVid, status, attr_count, attr_list); - } - else if (status != SAI_STATUS_SUCCESS) - { - sendApiResponse(api, status); - - if (info->isobjectid && api == SAI_COMMON_API_SET) - { - sai_object_id_t vid = metaKey.objectkey.key.object_id; - sai_object_id_t rid = m_translator->translateVidToRid(vid); - - SWSS_LOG_ERROR("VID: %s RID: %s", - sai_serialize_object_id(vid).c_str(), - sai_serialize_object_id(rid).c_str()); - } - - for (const auto &v: values) - { - SWSS_LOG_ERROR("attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str()); - } - - if (!m_enableSyncMode) - { - // throw only when sync mode is not enabled - - SWSS_LOG_THROW("failed to execute api: %s, key: %s, status: %s", - op.c_str(), - key.c_str(), - sai_serialize_status(status).c_str()); - } - } - else // non GET api, status is SUCCESS - { - sendApiResponse(api, status); - } - - syncUpdateRedisQuadEvent(status, api, kco); - - return status; -} - -sai_status_t Syncd::processOid( - _In_ sai_object_type_t objectType, - _In_ const std::string &strObjectId, - _In_ sai_common_api_t api, - _In_ uint32_t attr_count, - _In_ sai_attribute_t *attr_list) -{ - SWSS_LOG_ENTER(); - - sai_object_id_t object_id; - sai_deserialize_object_id(strObjectId, object_id); - - SWSS_LOG_DEBUG("calling %s for %s", - sai_serialize_common_api(api).c_str(), - sai_serialize_object_type(objectType).c_str()); - - /* - * We need to do translate vid/rid except for create, since create will - * create new RID value, and we will have to map them to VID we received in - * create query. - */ - - auto info = sai_metadata_get_object_type_info(objectType); - - if (info->isnonobjectid) - { - SWSS_LOG_THROW("passing non object id %s as generic object", info->objecttypename); - } - - switch (api) - { - case SAI_COMMON_API_CREATE: - return processOidCreate(objectType, strObjectId, attr_count, attr_list); - - case SAI_COMMON_API_REMOVE: - return processOidRemove(objectType, strObjectId); - - case SAI_COMMON_API_SET: - return processOidSet(objectType, strObjectId, attr_list); - - case SAI_COMMON_API_GET: - return processOidGet(objectType, strObjectId, attr_count, attr_list); - - default: - - SWSS_LOG_THROW("common api (%s) is not implemented", sai_serialize_common_api(api).c_str()); - } -} - -sai_status_t Syncd::processOidCreate( - _In_ sai_object_type_t objectType, - _In_ const std::string &strObjectId, - _In_ uint32_t attr_count, - _In_ sai_attribute_t *attr_list) -{ - SWSS_LOG_ENTER(); - - sai_object_id_t objectVid; - sai_deserialize_object_id(strObjectId, objectVid); - - // Object id is VID, we can use it to extract switch id. - - sai_object_id_t switchVid = VidManager::switchIdQuery(objectVid); - - sai_object_id_t switchRid = SAI_NULL_OBJECT_ID; - - if (objectType == SAI_OBJECT_TYPE_SWITCH) - { - SWSS_LOG_NOTICE("creating switch number %zu", m_switches.size() + 1); - } - else - { - /* - * When we are creating switch, then switchId parameter is ignored, but - * we can't convert it using vid to rid map, since rid doesn't exist - * yet, so skip translate for switch, but use translate for all other - * objects. - */ - - switchRid = m_translator->translateVidToRid(switchVid); - } - - sai_object_id_t objectRid; - - sai_status_t status = m_vendorSai->create(objectType, &objectRid, switchRid, attr_count, attr_list); - - if (status == SAI_STATUS_SUCCESS) - { - /* - * Object was created so new object id was generated we need to save - * virtual id's to redis db. - */ - - m_translator->insertRidAndVid(objectRid, objectVid); - - SWSS_LOG_INFO("saved VID %s to RID %s", - sai_serialize_object_id(objectVid).c_str(), - sai_serialize_object_id(objectRid).c_str()); - - if (objectType == SAI_OBJECT_TYPE_SWITCH) - { - /* - * All needed data to populate switch should be obtained inside SaiSwitch - * constructor, like getting all queues, ports, etc. - */ - - m_switches[switchVid] = std::make_shared(switchVid, objectRid, m_client, m_translator, m_vendorSai, false); - - m_mdioIpcServer->setSwitchId(objectRid); - - startDiagShell(objectRid); - } - - if (objectType == SAI_OBJECT_TYPE_PORT) - { - m_switches.at(switchVid)->onPostPortsCreate(1, &objectRid); - } - } - - return status; -} - -sai_status_t Syncd::processOidRemove( - _In_ sai_object_type_t objectType, - _In_ const std::string &strObjectId) -{ - SWSS_LOG_ENTER(); - - sai_object_id_t objectVid; - sai_deserialize_object_id(strObjectId, objectVid); - - sai_object_id_t rid = m_translator->translateVidToRid(objectVid); - - if (objectType == SAI_OBJECT_TYPE_PORT) - { - sai_object_id_t switchVid = VidManager::switchIdQuery(objectVid); - - m_switches.at(switchVid)->collectPortRelatedObjects(rid); - } - - sai_status_t status = m_vendorSai->remove(objectType, rid); - - if (status == SAI_STATUS_SUCCESS) - { - // remove all related objects from REDIS DB and also from existing - // object references since at this point they are no longer valid - - m_translator->eraseRidAndVid(rid, objectVid); - - if (objectType == SAI_OBJECT_TYPE_SWITCH) - { - /* - * On remove switch there should be extra action all local objects - * and redis object should be removed on remove switch local and - * redis db objects should be cleared. - * - * Currently we don't want to remove switch so we don't need this - * method, but lets put this as a safety check. - */ - - SWSS_LOG_THROW("remove switch is not implemented, FIXME"); - } - else - { - /* - * Removing some object succeeded. Let's check if that - * object was default created object, eg. vlan member. - * Then we need to update default created object map in - * SaiSwitch to be in sync, and be prepared for apply - * view to transfer those synced default created - * objects to temporary view when it will be created, - * since that will be out basic switch state. - * - * TODO: there can be some issues with reference count - * like for schedulers on scheduler groups since they - * should have internal references, and we still need - * to create dependency tree from saiDiscovery and - * update those references to track them, this is - * printed in metadata sanitycheck as "default value - * needs to be stored". - * - * TODO lets add SAI metadata flag for that this will - * also needs to be of internal/vendor default but we - * can already deduce that. - */ - - sai_object_id_t switchVid = VidManager::switchIdQuery(objectVid); - - if (m_switches.at(switchVid)->isDiscoveredRid(rid)) - { - m_switches.at(switchVid)->removeExistingObjectReference(rid); - } - - if (objectType == SAI_OBJECT_TYPE_PORT) - { - m_switches.at(switchVid)->postPortRemove(rid); - } - } - } - - return status; -} - -sai_status_t Syncd::processOidSet( - _In_ sai_object_type_t objectType, - _In_ const std::string &strObjectId, - _In_ sai_attribute_t *attr) -{ - SWSS_LOG_ENTER(); - - sai_object_id_t objectVid; - sai_deserialize_object_id(strObjectId, objectVid); - - sai_object_id_t rid = m_translator->translateVidToRid(objectVid); - - sai_status_t status = m_vendorSai->set(objectType, rid, attr); - - if (Workaround::isSetAttributeWorkaround(objectType, attr->id, status)) - { - return SAI_STATUS_SUCCESS; - } - - return status; -} - -sai_status_t Syncd::processOidGet( - _In_ sai_object_type_t objectType, - _In_ const std::string &strObjectId, - _In_ uint32_t attr_count, - _In_ sai_attribute_t *attr_list) -{ - SWSS_LOG_ENTER(); - - sai_object_id_t objectVid; - sai_deserialize_object_id(strObjectId, objectVid); - - sai_object_id_t rid = m_translator->translateVidToRid(objectVid); - - return m_vendorSai->get(objectType, rid, attr_count, attr_list); -} - -const char* Syncd::profileGetValue( - _In_ sai_switch_profile_id_t profile_id, - _In_ const char* variable) -{ - SWSS_LOG_ENTER(); - - if (variable == NULL) - { - SWSS_LOG_WARN("variable is null"); - return NULL; - } - - auto it = m_profileMap.find(variable); - - if (it == m_profileMap.end()) - { - SWSS_LOG_NOTICE("%s: NULL", variable); - return NULL; - } - - SWSS_LOG_NOTICE("%s: %s", variable, it->second.c_str()); - - return it->second.c_str(); -} - -int Syncd::profileGetNextValue( - _In_ sai_switch_profile_id_t profile_id, - _Out_ const char** variable, - _Out_ const char** value) -{ - SWSS_LOG_ENTER(); - - if (value == NULL) - { - SWSS_LOG_INFO("resetting profile map iterator"); - - m_profileIter = m_profileMap.begin(); - return 0; - } - - if (variable == NULL) - { - SWSS_LOG_WARN("variable is null"); - return -1; - } - - if (m_profileIter == m_profileMap.end()) - { - SWSS_LOG_INFO("iterator reached end"); - return -1; - } - - *variable = m_profileIter->first.c_str(); - *value = m_profileIter->second.c_str(); - - SWSS_LOG_INFO("key: %s:%s", *variable, *value); - - m_profileIter++; - - return 0; -} - -void Syncd::loadProfileMap() -{ - SWSS_LOG_ENTER(); - - // in case of virtual switch, populate context config - m_profileMap[SAI_KEY_VS_GLOBAL_CONTEXT] = std::to_string(m_commandLineOptions->m_globalContext); - m_profileMap[SAI_KEY_VS_CONTEXT_CONFIG] = m_commandLineOptions->m_contextConfig; - - if (m_commandLineOptions->m_profileMapFile.size() == 0) - { - SWSS_LOG_NOTICE("profile map file not specified"); - return; - } - - std::ifstream profile(m_commandLineOptions->m_profileMapFile); - - if (!profile.is_open()) - { - SWSS_LOG_ERROR("failed to open profile map file: %s: %s", - m_commandLineOptions->m_profileMapFile.c_str(), - strerror(errno)); - - exit(EXIT_FAILURE); - } - - // Provide default value at boot up time and let sai profile value - // Override following values if existing. - // SAI reads these values at start up time. It would be too late to - // set these values later when WARM BOOT is detected. - - m_profileMap[SAI_KEY_WARM_BOOT_WRITE_FILE] = DEF_SAI_WARM_BOOT_DATA_FILE; - m_profileMap[SAI_KEY_WARM_BOOT_READ_FILE] = DEF_SAI_WARM_BOOT_DATA_FILE; - - std::string line; - - while (getline(profile, line)) - { - if (line.size() > 0 && (line[0] == '#' || line[0] == ';')) - { - continue; - } - - size_t pos = line.find("="); - - if (pos == std::string::npos) - { - SWSS_LOG_WARN("not found '=' in line %s", line.c_str()); - continue; - } - - std::string key = line.substr(0, pos); - std::string value = line.substr(pos + 1); - - m_profileMap[key] = value; - - SWSS_LOG_INFO("insert: %s:%s", key.c_str(), value.c_str()); - } -} - -void Syncd::sendGetResponse( - _In_ sai_object_type_t objectType, - _In_ const std::string& strObjectId, - _In_ sai_object_id_t switchVid, - _In_ sai_status_t status, - _In_ uint32_t attr_count, - _In_ sai_attribute_t *attr_list) -{ - SWSS_LOG_ENTER(); - - std::vector entry; - - if (status == SAI_STATUS_SUCCESS) - { - m_translator->translateRidToVid(objectType, switchVid, attr_count, attr_list); - - /* - * Normal serialization + translate RID to VID. - */ - - entry = SaiAttributeList::serialize_attr_list( - objectType, - attr_count, - attr_list, - false); - - /* - * All oid values here are VIDs. - */ - - snoopGetResponse(objectType, strObjectId, attr_count, attr_list); - } - else if (status == SAI_STATUS_BUFFER_OVERFLOW) - { - /* - * In this case we got correct values for list, but list was too small - * so serialize only count without list itself, sairedis will need to - * take this into account when deserialize. - * - * If there was a list somewhere, count will be changed to actual value - * different attributes can have different lists, many of them may - * serialize only count, and will need to support that on the receiver. - */ - - entry = SaiAttributeList::serialize_attr_list( - objectType, - attr_count, - attr_list, - true); - } - else - { - /* - * Some other error, don't send attributes at all. - */ - } - - for (const auto &e: entry) - { - SWSS_LOG_DEBUG("attr: %s: %s", fvField(e).c_str(), fvValue(e).c_str()); - } - - std::string strStatus = sai_serialize_status(status); - - SWSS_LOG_INFO("sending response for GET api with status: %s", strStatus.c_str()); - - /* - * Since we have only one get at a time, we don't have to serialize object - * type and object id, only get status is required to be returned. Get - * response will not put any data to table, only queue is used. - */ - - m_selectableChannel->set(strStatus, entry, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); - - SWSS_LOG_INFO("response for GET api was send"); -} - -void Syncd::sendBulkGetResponse( - _In_ sai_object_type_t objectType, - _In_ const std::vector& strObjectIds, - _In_ sai_status_t status, - _In_ const std::vector>& attributes, - _In_ const std::vector& statuses) -{ - SWSS_LOG_ENTER(); - - std::vector entries; - entries.reserve(strObjectIds.size()); - - for (uint32_t idx = 0; idx < strObjectIds.size(); idx++) - { - const auto objectStatus = statuses[idx]; - const auto objectStatusStr = sai_serialize_status(statuses[idx]); - - if (objectStatus == SAI_STATUS_SUCCESS) - { - sai_object_id_t objectId{}; - sai_deserialize_object_id(strObjectIds[idx], objectId); - const auto switchVid = VidManager::switchIdQuery(objectId); - m_translator->translateRidToVid(objectType, switchVid, attributes[idx]->get_attr_count(), attributes[idx]->get_attr_list()); - - const auto entry = SaiAttributeList::serialize_attr_list(objectType, attributes[idx]->get_attr_count(), attributes[idx]->get_attr_list(), false); - const auto joined = Globals::joinFieldValues(entry); - - // Object IDs are not serialized. The attributes are assumed to be in order the object IDs were passed. - // Essentially, only status and attribute list is needed to be serialized and sent. - swss::FieldValueTuple fvt(objectStatusStr, joined); - - entries.push_back(fvt); - - /* - * All oid values here are VIDs. - */ - - snoopGetResponse(objectType, strObjectIds[idx], attributes[idx]->get_attr_count(), attributes[idx]->get_attr_list()); - } - else if (objectStatus == SAI_STATUS_BUFFER_OVERFLOW) - { - const auto entry = SaiAttributeList::serialize_attr_list(objectType, attributes[idx]->get_attr_count(), attributes[idx]->get_attr_list(), true); - const auto joined = Globals::joinFieldValues(entry); - - swss::FieldValueTuple fvt(objectStatusStr, joined); - - entries.push_back(fvt); - } - else - { - swss::FieldValueTuple fvt(objectStatusStr, Globals::joinFieldValues({})); - - entries.push_back(fvt); - } - } - - for (const auto &e: entries) - { - SWSS_LOG_DEBUG("attr: %s: %s", fvField(e).c_str(), fvValue(e).c_str()); - } - - const auto strStatus = sai_serialize_status(status); - - SWSS_LOG_INFO("sending response for bulk GET api with status: %s", strStatus.c_str()); - - m_selectableChannel->set(strStatus, entries, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); - - SWSS_LOG_INFO("response for bulk GET api was send"); -} - -void Syncd::snoopGetResponse( - _In_ sai_object_type_t object_type, - _In_ const std::string& strObjectId, // can be non object id - _In_ uint32_t attr_count, - _In_ const sai_attribute_t *attr_list) -{ - SWSS_LOG_ENTER(); - - /* - * NOTE: this method is operating on VIDs, all RIDs were translated outside - * this method. - */ - - /* - * Vlan (including vlan 1) will need to be put into TEMP view this should - * also be valid for all objects that were queried. - */ - - for (uint32_t idx = 0; idx < attr_count; ++idx) - { - const sai_attribute_t &attr = attr_list[idx]; - - auto meta = sai_metadata_get_attr_metadata(object_type, attr.id); - - if (meta == NULL) - { - SWSS_LOG_THROW("unable to get metadata for object type %d, attribute %d", object_type, attr.id); - } - - /* - * We should snoop oid values even if they are readonly we just note in - * temp view that those objects exist on switch. - */ - - switch (meta->attrvaluetype) - { - case SAI_ATTR_VALUE_TYPE_OBJECT_ID: - snoopGetOid(attr.value.oid); - break; - - case SAI_ATTR_VALUE_TYPE_OBJECT_LIST: - snoopGetOidList(attr.value.objlist); - break; - - case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID: - if (attr.value.aclfield.enable) - snoopGetOid(attr.value.aclfield.data.oid); - break; - - case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST: - if (attr.value.aclfield.enable) - snoopGetOidList(attr.value.aclfield.data.objlist); - break; - - case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_ID: - if (attr.value.aclaction.enable) - snoopGetOid(attr.value.aclaction.parameter.oid); - break; - - case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST: - if (attr.value.aclaction.enable) - snoopGetOidList(attr.value.aclaction.parameter.objlist); - break; - - default: - - /* - * If in future new attribute with object id will be added this - * will make sure that we will need to add handler here. - */ - - if (meta->isoidattribute) - { - SWSS_LOG_THROW("attribute %s is object id, but not processed, FIXME", meta->attridname); - } - - break; - } - - if (SAI_HAS_FLAG_READ_ONLY(meta->flags)) - { - /* - * If value is read only, we skip it, since after syncd restart we - * won't be able to set/create it anyway. - */ - - continue; - } - - if (meta->objecttype == SAI_OBJECT_TYPE_PORT && - meta->attrid == SAI_PORT_ATTR_HW_LANE_LIST) - { - /* - * Skip port lanes for now since we don't create ports. - */ - - SWSS_LOG_INFO("skipping %s for %s", meta->attridname, strObjectId.c_str()); - continue; - } - - /* - * Put non readonly, and non oid attribute value to temp view. - * - * NOTE: This will also put create-only attributes to view, and after - * syncd hard reinit we will not be able to do "SET" on that attribute. - * - * Similar action can happen when we will do this on asicSet during - * apply view. - */ - - snoopGetAttrValue(strObjectId, meta, attr); - } -} - -void Syncd::snoopGetAttr( - _In_ sai_object_type_t objectType, - _In_ const std::string& strObjectId, - _In_ const std::string& attrId, - _In_ const std::string& attrValue) -{ - SWSS_LOG_ENTER(); - - std::string mk = sai_serialize_object_type(objectType) + ":" + strObjectId; - - sai_object_meta_key_t metaKey; - sai_deserialize_object_meta_key(mk, metaKey); - - if (isInitViewMode()) - { - m_client->setTempAsicObject(metaKey, attrId, attrValue); - } - else - { - m_client->setAsicObject(metaKey, attrId, attrValue); - } -} - -void Syncd::snoopGetOid( - _In_ sai_object_id_t vid) -{ - SWSS_LOG_ENTER(); - - if (vid == SAI_NULL_OBJECT_ID) - { - // if snooped oid is NULL then we don't need take any action - return; - } - - /* - * Check if object was previously discovered on this switch, then no need to update ASIC_STATE. - */ - if (!isInitViewMode()) - { - sai_object_id_t rid; - - if (m_translator->tryTranslateVidToRid(vid, rid)) - { - const auto switchVid = VidManager::switchIdQuery(vid); - - if (m_switches[switchVid]->isDiscoveredRid(rid)) - { - // Already discovered object. - return; - } - } - } - - /* - * We need use redis version of object type query here since we are - * operating on VID value, and syncd is compiled against real SAI - * implementation which has different function m_vendorSai->objectTypeQuery. - */ - - sai_object_type_t objectType = VidManager::objectTypeQuery(vid); - - std::string strVid = sai_serialize_object_id(vid); - - snoopGetAttr(objectType, strVid, "NULL", "NULL"); -} - -void Syncd::snoopGetOidList( - _In_ const sai_object_list_t& list) -{ - SWSS_LOG_ENTER(); - - for (uint32_t i = 0; i < list.count; i++) - { - snoopGetOid(list.list[i]); - } -} - -void Syncd::snoopGetAttrValue( - _In_ const std::string& strObjectId, - _In_ const sai_attr_metadata_t *meta, - _In_ const sai_attribute_t& attr) -{ - SWSS_LOG_ENTER(); - - std::string value = sai_serialize_attr_value(*meta, attr); - - SWSS_LOG_DEBUG("%s:%s", meta->attridname, value.c_str()); - - snoopGetAttr(meta->objecttype, strObjectId, meta->attridname, value); -} - -void Syncd::inspectAsic() -{ - SWSS_LOG_ENTER(); - - // Fetch all the keys from ASIC DB - // Loop through all the keys in ASIC DB - - for (const auto &key: m_client->getAsicStateKeys()) - { - // ASIC_STATE:objecttype:objectid (object id may contain ':') - - auto start = key.find_first_of(":"); - - if (start == std::string::npos) - { - SWSS_LOG_ERROR("invalid ASIC_STATE_TABLE %s: no start :", key.c_str()); - break; - } - - auto mk = key.substr(start + 1); - - sai_object_meta_key_t metaKey; - sai_deserialize_object_meta_key(mk, metaKey); - - // Find all the attrid from ASIC DB, and use them to query ASIC - - auto hash = m_client->getAttributesFromAsicKey(key); - - std::vector values; - - for (auto &kv: hash) - { - const std::string &skey = kv.first; - const std::string &svalue = kv.second; - - swss::FieldValueTuple fvt(skey, svalue); - - values.push_back(fvt); - } - - SaiAttributeList list(metaKey.objecttype, values, false); - - sai_attribute_t *attr_list = list.get_attr_list(); - - uint32_t attr_count = list.get_attr_count(); - - SWSS_LOG_DEBUG("attr count: %u", list.get_attr_count()); - - if (attr_count == 0) - { - // TODO: how to check ASIC on ASIC DB key with NULL:NULL hash - // just ignore for now - continue; - } - - m_translator->translateVidToRid(metaKey); - - sai_status_t status = m_vendorSai->get(metaKey, attr_count, attr_list); - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("failed to execute get api on %s: %s", - sai_serialize_object_meta_key(metaKey).c_str(), - sai_serialize_status(status).c_str()); - continue; - } - - SaiAttributeList redis_list(metaKey.objecttype, values, false); - - sai_attribute_t *redis_attr_list = redis_list.get_attr_list(); - - m_translator->translateVidToRid(metaKey.objecttype, attr_count, redis_attr_list); - - // compare fields and values from ASIC_DB and SAI response and log the difference - - for (uint32_t index = 0; index < attr_count; ++index) - { - const sai_attribute_t& attr = attr_list[index]; - - auto meta = sai_metadata_get_attr_metadata(metaKey.objecttype, attr.id); - - if (meta == NULL) - { - SWSS_LOG_ERROR("FATAL: failed to find metadata for object type %s and attr id %d", - sai_serialize_object_type(metaKey.objecttype).c_str(), - attr.id); - break; - } - - std::string strSaiAttrValue = sai_serialize_attr_value(*meta, attr, false); - - std::string strRedisAttrValue = sai_serialize_attr_value(*meta, redis_attr_list[index], false); - - if (strRedisAttrValue == strSaiAttrValue) - { - SWSS_LOG_INFO("matched %s REDIS and ASIC attr value '%s' with on %s", - meta->attridname, - strRedisAttrValue.c_str(), - sai_serialize_object_meta_key(metaKey).c_str()); - } - else - { - SWSS_LOG_ERROR("failed to match %s REDIS attr '%s' with ASIC attr '%s' for %s", - meta->attridname, - strRedisAttrValue.c_str(), - strSaiAttrValue.c_str(), - sai_serialize_object_meta_key(metaKey).c_str()); - } - } - } -} - -sai_status_t Syncd::processNotifySyncd( - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - auto& key = kfvKey(kco); - sai_status_t status = SAI_STATUS_SUCCESS; - auto redisNotifySyncd = sai_deserialize_redis_notify_syncd(key); - - if (redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_INVOKE_DUMP) - { - SWSS_LOG_NOTICE("Invoking SAI failure dump"); - std::string ret_str; - int ret = swss::exec(SAI_FAILURE_DUMP_SCRIPT, ret_str); - if (ret != 0) - { - SWSS_LOG_ERROR("Error in executing SAI failure dump %s", ret_str.c_str()); - status = SAI_STATUS_FAILURE; - } - sendNotifyResponse(status); - return status; - } - - if (!m_commandLineOptions->m_enableTempView) - { - SWSS_LOG_NOTICE("received %s, ignored since TEMP VIEW is not used, returning success", key.c_str()); - - sendNotifyResponse(SAI_STATUS_SUCCESS); - - return SAI_STATUS_SUCCESS; - } - - if (m_veryFirstRun && m_firstInitWasPerformed && redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_INIT_VIEW) - { - /* - * Make sure that when second INIT view arrives, then we will jump to - * next section, since second init view may create switch that already - * exists and will fail with creating multiple switches error. - */ - - m_veryFirstRun = false; - } - else if (m_veryFirstRun) - { - SWSS_LOG_NOTICE("very first run is TRUE, op = %s", key.c_str()); - - - /* - * On the very first start of syncd, "compile" view is directly applied - * on device, since it will make it easier to switch to new asic state - * later on when we restart orch agent. - */ - - if (redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_INIT_VIEW) - { - /* - * On first start we just do "apply" directly on asic so we set - * init to false instead of true. - */ - - m_asicInitViewMode = false; - - m_firstInitWasPerformed = true; - - // we need to clear current temp view to make space for new one - - clearTempView(); - - /* - * Transition to longer watchdog timeout in INIT_VIEW on Chassis Switch - * Wait for create:SAI_OBJECT_TYPE_SWITCH - * Then transition back in APPLY_VIEW - */ - transitionToInitWatchdogTimeout(); - } - else if (redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_APPLY_VIEW) - { - m_veryFirstRun = false; - - m_asicInitViewMode = false; -#ifdef MELLANOX - bool applyViewInFastFastBoot = m_commandLineOptions->m_startType == SAI_START_TYPE_FASTFAST_BOOT || - m_commandLineOptions->m_startType == SAI_START_TYPE_EXPRESS_BOOT || - m_commandLineOptions->m_startType == SAI_START_TYPE_FAST_BOOT; -#else - bool applyViewInFastFastBoot = m_commandLineOptions->m_startType == SAI_START_TYPE_FASTFAST_BOOT || - m_commandLineOptions->m_startType == SAI_START_TYPE_EXPRESS_BOOT; -#endif - if (applyViewInFastFastBoot) - { - // express/fastfast boot configuration end - - status = onApplyViewInFastFastBoot(); - } - - SWSS_LOG_NOTICE("setting very first run to FALSE, op = %s", key.c_str()); - - transitionToNormalWatchdogTimeout(); - } - else if (redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_INSPECT_ASIC) - { - SWSS_LOG_NOTICE("syncd switched to INSPECT ASIC mode"); - - transitionToInitWatchdogTimeout(); - - inspectAsic(); - - transitionToNormalWatchdogTimeout(); - - sendNotifyResponse(SAI_STATUS_SUCCESS); - } - else - { - SWSS_LOG_THROW("unknown operation: %s", key.c_str()); - } - - sendNotifyResponse(status); - - return status; - } - - if (redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_INIT_VIEW) - { - if (m_asicInitViewMode) - { - SWSS_LOG_WARN("syncd is already in asic INIT VIEW mode, but received init again, orchagent restarted before apply?"); - } - - m_asicInitViewMode = true; - - clearTempView(); - - m_createdInInitView.clear(); - - // NOTE: Currently as WARN to be easier to spot, later should be NOTICE. - - SWSS_LOG_WARN("syncd switched to INIT VIEW mode, all op will be saved to TEMP view"); - - /* - * Transition to longer watchdog timeout in INIT_VIEW on Chassis Switch - * Wait for create:SAI_OBJECT_TYPE_SWITCH - * Then transition back in APPLY_VIEW - */ - transitionToInitWatchdogTimeout(); - - sendNotifyResponse(SAI_STATUS_SUCCESS); - } - else if (redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_APPLY_VIEW) - { - m_asicInitViewMode = false; - - // NOTE: Currently as WARN to be easier to spot, later should be NOTICE. - - SWSS_LOG_WARN("syncd received APPLY VIEW, will translate"); - - try - { - status = applyView(); - } - catch(...) - { - /* - * If apply view will fail with exception, try to send fail - * response to sairedis, since later there can be switch shutdown - * notification sent, and it will be synchronized with mutex, and - * it will not be processed until get response timeout will hit. - */ - - sendNotifyResponse(SAI_STATUS_FAILURE); - - throw; - } - - transitionToNormalWatchdogTimeout(); - - sendNotifyResponse(status); - - if (status == SAI_STATUS_SUCCESS) - { - /* - * We successfully applied new view, VID mapping could change, so - * we need to clear local db, and all new VIDs will be queried - * using redis. - * - * TODO possible race condition - get notification when new view is - * applied and cache have old values, and notification start's - * translating vid/rid, we need to stop processing notifications - * for transition (queue can still grow), possible fdb - * notifications but fdb learning was disabled on warm boot, so - * there should be no issue. - */ - - m_translator->clearLocalCache(); - - m_createdInInitView.clear(); - } - else - { - /* - * Apply view failed. It can fail in 2 ways, ether nothing was - * executed, on asic, or asic is inconsistent state then we should - * die or hang. - */ - - return status; - } - } - else if (redisNotifySyncd == SAI_REDIS_NOTIFY_SYNCD_INSPECT_ASIC) - { - SWSS_LOG_NOTICE("syncd switched to INSPECT ASIC mode"); - - transitionToInitWatchdogTimeout(); - - inspectAsic(); - - transitionToNormalWatchdogTimeout(); - - sendNotifyResponse(SAI_STATUS_SUCCESS); - } - else - { - SWSS_LOG_ERROR("unknown operation: %s", key.c_str()); - - sendNotifyResponse(SAI_STATUS_NOT_IMPLEMENTED); - - SWSS_LOG_THROW("notify syncd %s operation failed", key.c_str()); - } - - return SAI_STATUS_SUCCESS; -} - -void Syncd::sendNotifyResponse( - _In_ sai_status_t status) -{ - SWSS_LOG_ENTER(); - - std::string strStatus = sai_serialize_status(status); - - std::vector entry; - - SWSS_LOG_INFO("sending response: %s", strStatus.c_str()); - - m_selectableChannel->set(strStatus, entry, REDIS_ASIC_STATE_COMMAND_NOTIFY); -} - -void Syncd::transitionToNormalWatchdogTimeout() -{ - SWSS_LOG_ENTER(); - - int64_t normalTimeout = m_commandLineOptions->m_watchdogWarnTimeSpan * WD_DELAY_FACTOR; - - m_timerWatchdog.setWarnTimespan(normalTimeout); -} - -void Syncd::transitionToInitWatchdogTimeout() -{ - SWSS_LOG_ENTER(); - - int64_t initTimeout = m_commandLineOptions->m_watchdogInitTimeSpan * WD_DELAY_FACTOR; - - m_timerWatchdog.setWarnTimespan(initTimeout); -} - -void Syncd::clearTempView() -{ - SWSS_LOG_ENTER(); - - SWSS_LOG_NOTICE("clearing current TEMP VIEW"); - - SWSS_LOG_TIMER("clear temp view"); - - m_client->removeTempAsicStateTable(); - - // Also clear list of objects removed in init view mode. - - m_initViewRemovedVidSet.clear(); -} - -sai_status_t Syncd::onApplyViewInFastFastBoot() -{ - SWSS_LOG_ENTER(); - - sai_status_t all = SAI_STATUS_SUCCESS; - - for (auto& kvp: m_switches) - { - sai_attribute_t attr; - - attr.id = SAI_SWITCH_ATTR_FAST_API_ENABLE; - attr.value.booldata = false; - - sai_status_t status = m_vendorSai->set(SAI_OBJECT_TYPE_SWITCH, kvp.second->getRid(), &attr); - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_FAST_API_ENABLE=false: %s for switch RID: %s", - sai_serialize_status(status).c_str(), - sai_serialize_object_id(kvp.second->getRid()).c_str()); - - all = status; - } - } - - return all; -} - -sai_status_t Syncd::applyView() -{ - SWSS_LOG_ENTER(); - - SWSS_LOG_TIMER("apply"); - - /* - * We assume that there will be no case that we will move from 1 to 0, also - * if at the beginning there is no switch, then when user will send create, - * and it will be actually created (real call) so there should be no case - * when we are moving from 0 -> 1. - */ - - /* - * This method contains 2 stages. - * - * First stage is non destructive, when orchagent will build new view, and - * there will be bug in comparison logic in first stage, then syncd will - * send failure when doing apply view to orchagent but it will still be - * running. No asic operations are performed during this stage. - * - * Second stage is destructive, so if there will be bug in comparison logic - * or any asic operation will fail, then syncd will crash, since asic will - * be in inconsistent state. - */ - - /* - * Initialize rand for future candidate object selection if necessary. - * - * NOTE: Should this be deterministic? So we could repeat random choice - * when something bad happen or we hit a bug, so in that case it will be - * easier for reproduce, we could at least log value returned from time(). - * - * TODO: To make it stable, we also need to make stable redisGetAsicView - * since now order of items is random. Also redis result needs to be - * sorted. - */ - - // Read current and temporary views from REDIS. - - auto currentMap = m_client->getAsicView(); - auto temporaryMap = m_client->getTempAsicView(); - - if (currentMap.size() != temporaryMap.size()) - { - SWSS_LOG_THROW("current view switches: %zu != temporary view switches: %zu, FATAL", - currentMap.size(), - temporaryMap.size()); - } - - if (currentMap.size() != m_switches.size()) - { - SWSS_LOG_THROW("current asic view switches %zu != defined switches %zu, FATAL", - currentMap.size(), - m_switches.size()); - } - - // VID of switches must match for each map - - for (auto& kvp: currentMap) - { - if (temporaryMap.find(kvp.first) == temporaryMap.end()) - { - SWSS_LOG_THROW("switch VID %s missing from temporary view!, FATAL", - sai_serialize_object_id(kvp.first).c_str()); - } - - if (m_switches.find(kvp.first) == m_switches.end()) - { - SWSS_LOG_THROW("switch VID %s missing from ASIC, FATAL", - sai_serialize_object_id(kvp.first).c_str()); - } - } - - std::vector> currentViews; - std::vector> tempViews; - std::vector> cls; - - try - { - for (auto& kvp: m_switches) - { - auto switchVid = kvp.first; - - auto sw = m_switches.at(switchVid); - - /* - * We are starting first stage here, it still can throw exceptions - * but it's non destructive for ASIC, so just catch and return in - * case of failure. - * - * Each ASIC view at this point will contain only 1 switch. - */ - - auto current = std::make_shared(currentMap.at(switchVid)); - auto temp = std::make_shared(temporaryMap.at(switchVid)); - - auto cl = std::make_shared(m_vendorSai, sw, m_handler, m_initViewRemovedVidSet, current, temp, m_breakConfig); - - cl->compareViews(); - - currentViews.push_back(current); - tempViews.push_back(temp); - cls.push_back(cl); - } - } - catch (const std::exception &e) - { - /* - * Exception was thrown in first stage, those were non destructive - * actions so just log exception and let syncd running. - */ - - SWSS_LOG_ERROR("Exception: %s", e.what()); - - return SAI_STATUS_FAILURE; - } - - /* - * This is second stage. Those operations are destructive, if any of them - * fail, then we will have inconsistent state in ASIC. - */ - - if (m_commandLineOptions->m_enableUnittests) - { - dumpComparisonLogicOutput(currentViews); - } - - for (auto& cl: cls) - { - cl->executeOperationsOnAsic(); // can throw, if so asic will be in inconsistent state - } - - updateRedisDatabase(tempViews); - - for (auto& cl: cls) - { - if (m_commandLineOptions->m_enableConsistencyCheck) - { - bool consistent = cl->checkAsicVsDatabaseConsistency(m_translator); - - if (!consistent && m_commandLineOptions->m_enableUnittests) - { - SWSS_LOG_THROW("ASIC content is different than DB content!"); - } - } - } - - return SAI_STATUS_SUCCESS; -} - -void Syncd::dumpComparisonLogicOutput( - _In_ const std::vector>& currentViews) -{ - SWSS_LOG_ENTER(); - - std::stringstream ss; - - size_t total = 0; // total operations from all switches - - for (auto& c: currentViews) - { - total += c->asicGetOperationsCount(); - } - - ss << "ASIC_OPERATIONS: " << total << std::endl; - - for (auto& c: currentViews) - { - ss << "ASIC_OPERATIONS on " - << sai_serialize_object_id(c->getSwitchVid()) - << " : " - << c->asicGetOperationsCount() - << std::endl; - - for (const auto &op: c->asicGetWithOptimizedRemoveOperations()) - { - const std::string &key = kfvKey(*op.m_op); - const std::string &opp = kfvOp(*op.m_op); - - ss << "o " << opp << ": " << key << std::endl; - - const auto &values = kfvFieldsValues(*op.m_op); - - for (auto v: values) - ss << "a: " << fvField(v) << " " << fvValue(v) << std::endl; - } - } - - std::ofstream log("applyview.log"); - - if (log.is_open()) - { - log << ss.str(); - - log.close(); - - SWSS_LOG_NOTICE("wrote apply_view asic operations to applyview.log"); - } - else - { - SWSS_LOG_ERROR("failed to open applyview.log"); - } -} - -void Syncd::updateRedisDatabase( - _In_ const std::vector>& temporaryViews) -{ - SWSS_LOG_ENTER(); - - // TODO: We can make LUA script for this which will be much faster. - // - // TODO: Needs to be revisited if ASIC views will be across multiple redis - // database indexes. - - SWSS_LOG_TIMER("redis update"); - - m_client->removeAsicStateTable(); - - m_client->removeTempAsicStateTable(); - - // Save temporary views as current view in redis database. - - for (auto& tv: temporaryViews) - { - for (const auto &pair: tv->m_soAll) - { - const auto &obj = pair.second; - - const auto &attr = obj->getAllAttributes(); - - std::vector entry; - - for (const auto &ap: attr) - { - const auto saiAttr = ap.second; - - entry.emplace_back(saiAttr->getStrAttrId(), saiAttr->getStrAttrValue()); - } - - m_client->createAsicObject(obj->m_meta_key, entry); - } - } - - /* - * Remove previous RID2VID maps and apply new map. - * - * NOTE: This needs to be done per switch, we can't remove all maps. - */ - - // TODO check if those 2 maps are consistent - - std::unordered_map allVid2Rid; - - for (auto& tv: temporaryViews) - { - for (auto &kv: tv->m_ridToVid) - { - allVid2Rid[kv.second] = kv.first; - } - } - - m_client->setVidAndRidMap(allVid2Rid); - - SWSS_LOG_NOTICE("updated redis database"); -} - -// TODO for future we can have each switch in separate redis db index or even -// some switches in the same db index and some in separate. Current redis get -// asic view is assuming all switches are in the same db index an also some -// operations per switch are accessing data base in SaiSwitch class. This -// needs to be reorganised to access database per switch basis and get only -// data that corresponds to each particular switch and access correct db index. - -void Syncd::onSyncdStart( - _In_ bool warmStart) -{ - SWSS_LOG_ENTER(); - - std::lock_guard lock(m_mutex); - - /* - * It may happen that after initialize we will receive some port - * notifications with port'ids that are not in redis db yet, so after - * checking VIDTORID map there will be entries and translate_vid_to_rid - * will generate new id's for ports, this may cause race condition so we - * need to use a lock here to prevent that. - */ - - SWSS_LOG_TIMER("on syncd start"); - - if (warmStart) - { - /* - * Switch was warm started, so switches map is empty, we need to - * recreate it based on existing entries inside database. - * - * Currently we expect only one switch, then we need to call it. - * - * Also this will make sure that current switch id is the same as - * before restart. - * - * If we want to support multiple switches, this needs to be adjusted. - */ - - performWarmRestart(); - - SWSS_LOG_NOTICE("skipping hard reinit since WARM start was performed"); - return; - } - - SWSS_LOG_NOTICE("performing hard reinit since COLD start was performed"); - - /* - * Switch was restarted in hard way, we need to perform hard reinit and - * recreate switches map. - */ - - if (m_switches.size()) - { - SWSS_LOG_THROW("performing hard reinit, but there are %zu switches defined, bug!", m_switches.size()); - } - - HardReiniter hr(m_client, m_translator, m_vendorSai, m_handler); - - m_switches = hr.hardReinit(); - - for (auto& sw: m_switches) - { - startDiagShell(sw.second->getRid()); - } - - SWSS_LOG_NOTICE("hard reinit succeeded"); -} - -void Syncd::onSwitchCreateInInitViewMode( - _In_ sai_object_id_t switchVid, - _In_ uint32_t attr_count, - _In_ const sai_attribute_t *attr_list) -{ - SWSS_LOG_ENTER(); - - /* - * We can have multiple switches here, but each switch is identified by - * SAI_SWITCH_ATTR_SWITCH_HARDWARE_INFO. This attribute is treated as key, - * so each switch will have different hardware info. - * - * Currently we assume that we have only one switch. - * - * We can have 2 scenarios here: - * - * - we have multiple switches already existing, and in init view mode user - * will create the same switches, then since switch id are deterministic - * we can match them by hardware info and by switch id, it may happen - * that switch id will be different if user will create switches in - * different order, this case will be not supported unless special logic - * will be written to handle that case. This case is solved by bounding - * hardware info to switch index in context config file. - * - * - if user created switches but non of switch has the same hardware info - * then it means we need to create actual switch here, since user will - * want to query switch ports etc values, that's why on create switch is - * special case, and that's why we need to keep track of all switches. - * This case is also solved bu allowing creation of only switches defined - * in context config which bounds hardware info and switch index making - * switch VID deterministic. - * - * Since we are creating switch here, we are sure that this switch don't - * have any oid attributes set, so we can pass all attributes. - * - * Hardware info attribute must be passed and all non OID attributes - * including create only and conditionals. - */ - - /* - * Multiple switches scenario with changed order: - * - * If orchagent will create the same switch with the same hardware info but - * with different order since switch id is deterministic, then VID of both - * switches will always match since they are bound to hardware info using - * context config file. - */ - - if (m_switches.find(switchVid) == m_switches.end()) - { - /* - * Switch with particular VID don't exists yet, so lets create it. We - * need to create this switch so user in init mode could query switch - * properties using GET api. - * - * We assume that none of attributes is object id attribute. - * - * This scenario can happen when you start syncd on empty database and - * then you quit and restart it again. - */ - - sai_object_id_t switchRid; - - sai_status_t status; - - { - SWSS_LOG_TIMER("cold boot: create switch"); - - status = m_vendorSai->create(SAI_OBJECT_TYPE_SWITCH, &switchRid, 0, attr_count, attr_list); - } - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_THROW("failed to create switch in init view mode: %s", - sai_serialize_status(status).c_str()); - } - - /* - * Object was created so new RID was generated we need to save virtual - * id's to redis db. - */ - - SWSS_LOG_NOTICE("created switch VID %s to RID %s in init view mode", - sai_serialize_object_id(switchVid).c_str(), - sai_serialize_object_id(switchRid).c_str()); - - m_translator->insertRidAndVid(switchRid, switchVid); - - // make switch initialization and get all default data - - m_switches[switchVid] = std::make_shared(switchVid, switchRid, m_client, m_translator, m_vendorSai, false); - - m_mdioIpcServer->setSwitchId(switchRid); - - startDiagShell(switchRid); - } - else - { - /* - * There is already switch defined, we need to match it by hardware - * info and we need to know that current switch VID also should match - * since it's deterministic created. - */ - - auto sw = m_switches.at(switchVid); - - // switches VID must match, since it's deterministic - - if (switchVid != sw->getVid()) - { - SWSS_LOG_THROW("created switch VID don't match: previous %s, current: %s", - sai_serialize_object_id(switchVid).c_str(), - sai_serialize_object_id(sw->getVid()).c_str()); - } - - // also hardware info also must match - - std::string currentHw = sw->getHardwareInfo(); - std::string newHw; - - auto attr = sai_metadata_get_attr_by_id(SAI_SWITCH_ATTR_SWITCH_HARDWARE_INFO, attr_count, attr_list); - - if (attr == NULL) - { - // this is ok, attribute doesn't exist, so assumption is empty string - } - else - { - newHw = std::string((char*)attr->value.s8list.list, attr->value.s8list.count); - } - - SWSS_LOG_NOTICE("new switch %s contains hardware info: '%s'", - sai_serialize_object_id(switchVid).c_str(), - newHw.c_str()); - - /* - * The line below is added due to a behavior change of SAI call. - * - * TODO: remove the line when SAI vendor agrees fix on their end. - */ - currentHw = currentHw == "none"? "" : currentHw; - - if (currentHw != newHw) - { - SWSS_LOG_THROW("hardware info mismatch: current '%s' vs new '%s'", currentHw.c_str(), newHw.c_str()); - } - - SWSS_LOG_NOTICE("current %s switch hardware info: '%s'", - sai_serialize_object_id(switchVid).c_str(), - currentHw.c_str()); - - /* - * Some attributes on new switch could be different then on existing - * one, but we are in init view mode so comparison logic will be - * executed on apply view and those attributes will be compared and - * actions will be generated if any of them are different. - */ - } -} - -void Syncd::performWarmRestartSingleSwitch( - _In_ const std::string& key) -{ - SWSS_LOG_ENTER(); - - // key should be in format ASIC_STATE:SAI_OBJECT_TYPE_SWITCH:oid:0xYYYY - - /* - * Since multiple switches can be defined on warm boot, then we need to - * correctly identify each switch by passing hardware info. - * - * TODO: do we also need to pass any other attributes, like create only etc? - */ - - auto start = key.find_first_of(":") + 1; - auto end = key.find(":", start); - - std::string strSwitchVid = key.substr(end + 1); - - std::vector values; - - auto hash = m_client->getAttributesFromAsicKey(key); - - SWSS_LOG_NOTICE("switch %s", strSwitchVid.c_str()); - - for (auto &kv: hash) - { - const std::string& skey = kv.first; - const std::string& svalue = kv.second; - - if (skey == "NULL") - continue; - - SWSS_LOG_NOTICE(" - attr: %s:%s", skey.c_str(), svalue.c_str()); - - swss::FieldValueTuple fvt(skey, svalue); - - values.push_back(fvt); - } - - SaiAttributeList list(SAI_OBJECT_TYPE_SWITCH, values, false); - - sai_object_id_t switchVid; - - sai_deserialize_object_id(strSwitchVid, switchVid); - - sai_object_id_t originalSwitchRid = m_translator->translateVidToRid(switchVid); - - sai_object_id_t switchRid; - - std::vector attrs; - - sai_attribute_t attr; - - attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; - attr.value.booldata = true; - - attrs.push_back(attr); - - sai_attribute_t *attrList = list.get_attr_list(); - - uint32_t attrCount = list.get_attr_count(); - - for (uint32_t idx = 0; idx < attrCount; idx++) - { - auto id = attrList[idx].id; - - if (id == SAI_SWITCH_ATTR_INIT_SWITCH) - continue; - - auto meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_SWITCH, id); - - /* - * If we want to handle multiple switches, then during warm boot switch - * create we need to pass hardware info so vendor sai could know which - * switch to initialize. We also need to update pointer values since - * new process could be loaded at different address space. - */ - - if (id == SAI_SWITCH_ATTR_SWITCH_HARDWARE_INFO || meta->attrvaluetype == SAI_ATTR_VALUE_TYPE_POINTER) - { - attrs.push_back(attrList[idx]); - continue; - } - - SWSS_LOG_NOTICE("skipping warm boot: %s", meta->attridname); - } - - // TODO support multiple notification handlers - m_handler->updateNotificationsPointers((uint32_t)attrs.size(), attrs.data()); - - sai_status_t status; - - { - SWSS_LOG_TIMER("Warm boot: create switch VID: %s", sai_serialize_object_id(switchVid).c_str()); - - status = m_vendorSai->create(SAI_OBJECT_TYPE_SWITCH, &switchRid, 0, (uint32_t)attrs.size(), attrs.data()); - } - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_THROW("failed to create switch RID: %s for VID %s", - sai_serialize_status(status).c_str(), - sai_serialize_object_id(switchVid).c_str()); - } - - if (originalSwitchRid != switchRid) - { - SWSS_LOG_THROW("Unexpected RID 0x%" PRIx64 " (expected 0x%" PRIx64 " )", - switchRid, originalSwitchRid); - } - - // perform all get operations on existing switch - - auto sw = m_switches[switchVid] = std::make_shared(switchVid, switchRid, m_client, m_translator, m_vendorSai, true); - - startDiagShell(switchRid); -} - -void Syncd::performWarmRestart() -{ - SWSS_LOG_ENTER(); - - /* - * There should be no case when we are doing warm restart and there is no - * switch defined, we will throw at such a case. - * - * This case could be possible when no switches were created and only api - * was initialized, but we will skip this scenario and address is when we - * will have need for it. - */ - - auto entries = m_client->getAsicStateSwitchesKeys(); - - if (entries.size() == 0) - { - SWSS_LOG_THROW("on warm restart there is no switches defined in DB, not supported yet, FIXME"); - } - - SWSS_LOG_NOTICE("switches defined in warm restart: %zu", entries.size()); - - // here we could have multiple switches defined, let's process them one by one - - for (auto& entry: entries) - { - performWarmRestartSingleSwitch(entry); - } -} - -void Syncd::startDiagShell( - _In_ sai_object_id_t switchRid) -{ - SWSS_LOG_ENTER(); - - if (m_commandLineOptions->m_enableDiagShell) - { - SWSS_LOG_NOTICE("starting diag shell thread for switch RID %s", - sai_serialize_object_id(switchRid).c_str()); - - std::thread thread = std::thread(&Syncd::diagShellThreadProc, this, switchRid); - - thread.detach(); - } -} - -void Syncd::diagShellThreadProc( - _In_ sai_object_id_t switchRid) -{ - SWSS_LOG_ENTER(); - - sai_status_t status; - - /* - * This is currently blocking API on broadcom, it will block until we exit - * shell. - */ - - while (true) - { - sai_attribute_t attr; - attr.id = SAI_SWITCH_ATTR_SWITCH_SHELL_ENABLE; - attr.value.booldata = true; - - status = m_vendorSai->set(SAI_OBJECT_TYPE_SWITCH, switchRid, &attr); - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to enable switch shell: %s", - sai_serialize_status(status).c_str()); - return; - } - - sleep(1); - } -} - -void Syncd::sendShutdownRequest( - _In_ sai_object_id_t switchVid) -{ - SWSS_LOG_ENTER(); - - if (m_notifications == nullptr) - { - SWSS_LOG_WARN("notifications pointer is NULL"); - return; - } - - auto s = sai_serialize_object_id(switchVid); - - SWSS_LOG_NOTICE("sending switch_shutdown_request notification to OA for switch: %s", s.c_str()); - - std::vector entry; - - // TODO use m_handler->onSwitchShutdownRequest(switchVid); (but this should be per switch) - - s = sai_serialize_switch_shutdown_request(switchVid); - - m_notifications->send(SAI_SWITCH_NOTIFICATION_NAME_SWITCH_SHUTDOWN_REQUEST, s, entry); -} - -void Syncd::sendShutdownRequestAfterException() -{ - SWSS_LOG_ENTER(); - - std::lock_guard lock(m_mutex); - - try - { - if (m_switches.size()) - { - for (auto& kvp: m_switches) - { - sendShutdownRequest(kvp.second->getVid()); - } - } - else - { - sendShutdownRequest(SAI_NULL_OBJECT_ID); - } - - SWSS_LOG_NOTICE("notification send successfully"); - } - catch(const std::exception &e) - { - SWSS_LOG_ERROR("Runtime error: %s", e.what()); - } - catch(...) - { - SWSS_LOG_ERROR("Unknown runtime error"); - } -} - -void Syncd::saiLoglevelNotify( - _In_ std::string strApi, - _In_ std::string strLogLevel) -{ - SWSS_LOG_ENTER(); - - try - { - sai_log_level_t logLevel; - sai_deserialize_log_level(strLogLevel, logLevel); - - sai_api_t api; - sai_deserialize_api(strApi, api); - - sai_status_t status = m_vendorSai->logSet(api, logLevel); - - if (status == SAI_STATUS_SUCCESS) - { - SWSS_LOG_NOTICE("Setting SAI loglevel %s on %s", strLogLevel.c_str(), strApi.c_str()); - } - else - { - SWSS_LOG_INFO("set loglevel failed: %s", sai_serialize_status(status).c_str()); - } - } - catch (const std::exception& e) - { - SWSS_LOG_ERROR("Failed to set loglevel to %s on %s: %s", - strLogLevel.c_str(), - strApi.c_str(), - e.what()); - } -} - -void Syncd::setSaiApiLogLevel() -{ - SWSS_LOG_ENTER(); - - // We start from 1 since 0 is SAI_API_UNSPECIFIED. - - for (uint32_t idx = 1; idx < sai_metadata_enum_sai_api_t.valuescount; ++idx) - { - // NOTE: link to db is singleton, so if we would want multiple Syncd - // instances running at the same process, we need to have logger - // registrar similar to net link messages - - swss::Logger::linkToDb( - sai_metadata_enum_sai_api_t.valuesnames[idx], - std::bind(&Syncd::saiLoglevelNotify, this, _1, _2), - sai_serialize_log_level(SAI_LOG_LEVEL_NOTICE)); - } -} - -sai_status_t Syncd::removeAllSwitches() -{ - SWSS_LOG_ENTER(); - - SWSS_LOG_NOTICE("Removing all switches"); - - // TODO mutex ? - - sai_status_t result = SAI_STATUS_SUCCESS; - - for (auto& sw: m_switches) - { - auto rid = sw.second->getRid(); - - auto strRid = sai_serialize_object_id(rid); - - SWSS_LOG_TIMER("removing switch RID %s", strRid.c_str()); - - auto status = m_vendorSai->remove(SAI_OBJECT_TYPE_SWITCH, rid); - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_NOTICE("Can't delete a switch RID %s: %s", - strRid.c_str(), - sai_serialize_status(status).c_str()); - - result = status; - } - } - - return result; -} - -sai_status_t Syncd::setRestartWarmOnAllSwitches( - _In_ bool flag) -{ - SWSS_LOG_ENTER(); - - sai_status_t result = SAI_STATUS_SUCCESS; - - sai_attribute_t attr; - - attr.id = SAI_SWITCH_ATTR_RESTART_WARM; - attr.value.booldata = flag; - - for (auto& sw: m_switches) - { - auto rid = sw.second->getRid(); - - auto strRid = sai_serialize_object_id(rid); - - auto status = m_vendorSai->set(SAI_OBJECT_TYPE_SWITCH, rid, &attr); - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_RESTART_WARM=%s: %s:%s", - (flag ? "true" : "false"), - strRid.c_str(), - sai_serialize_status(status).c_str()); - - result = status; - } - } - - return result; -} - -sai_status_t Syncd::setFastAPIEnableOnAllSwitches() -{ - SWSS_LOG_ENTER(); - - sai_status_t result = SAI_STATUS_SUCCESS; - - sai_attribute_t attr; - - attr.id = SAI_SWITCH_ATTR_FAST_API_ENABLE; - attr.value.booldata = true; - - for (auto& sw: m_switches) - { - auto rid = sw.second->getRid(); - - auto strRid = sai_serialize_object_id(rid); - - auto status = m_vendorSai->set(SAI_OBJECT_TYPE_SWITCH, rid, &attr); - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_PRE_SHUTDOWN=true: %s:%s", - strRid.c_str(), - sai_serialize_status(status).c_str()); - - result = status; - break; - } - } - - return result; -} - -sai_status_t Syncd::setPreShutdownOnAllSwitches() -{ - SWSS_LOG_ENTER(); - - sai_status_t result = SAI_STATUS_SUCCESS; - - sai_attribute_t attr; - - attr.id = SAI_SWITCH_ATTR_PRE_SHUTDOWN; - attr.value.booldata = true; - - for (auto& sw: m_switches) - { - auto rid = sw.second->getRid(); - - auto strRid = sai_serialize_object_id(rid); - - auto status = m_vendorSai->set(SAI_OBJECT_TYPE_SWITCH, rid, &attr); - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_PRE_SHUTDOWN=true: %s:%s", - strRid.c_str(), - sai_serialize_status(status).c_str()); - - result = status; - } - } - - return result; -} - -sai_status_t Syncd::setUninitDataPlaneOnRemovalOnAllSwitches() -{ - SWSS_LOG_ENTER(); - - SWSS_LOG_NOTICE("Fast/warm reboot requested, keeping data plane running"); - - sai_status_t result = SAI_STATUS_SUCCESS; - - sai_attribute_t attr; - - attr.id = SAI_SWITCH_ATTR_UNINIT_DATA_PLANE_ON_REMOVAL; - attr.value.booldata = false; - - for (auto& sw: m_switches) - { - auto rid = sw.second->getRid(); - - auto strRid = sai_serialize_object_id(rid); - - sai_attr_capability_t attr_capability = {}; - - sai_status_t queryStatus; - - queryStatus = m_vendorSai->queryAttributeCapability(rid, - SAI_OBJECT_TYPE_SWITCH, - SAI_SWITCH_ATTR_UNINIT_DATA_PLANE_ON_REMOVAL, - &attr_capability); - if (queryStatus != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to get SAI_SWITCH_ATTR_UNINIT_DATA_PLANE_ON_REMOVAL capabilities: %s:%s", - strRid.c_str(), - sai_serialize_status(queryStatus).c_str()); - - result = queryStatus; - continue; - } - - if (attr_capability.set_implemented) - { - auto status = m_vendorSai->set(SAI_OBJECT_TYPE_SWITCH, rid, &attr); - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_UNINIT_DATA_PLANE_ON_REMOVAL=false: %s:%s", - strRid.c_str(), - sai_serialize_status(status).c_str()); - - result = status; - } - } - } - - return result; -} - -void Syncd::syncProcessNotification( - _In_ const swss::KeyOpFieldsValuesTuple& item) -{ - std::lock_guard lock(m_mutex); - - SWSS_LOG_ENTER(); - - m_processor->syncProcessNotification(item); -} - -bool Syncd::isVeryFirstRun() -{ - SWSS_LOG_ENTER(); - - /* - * If lane map is not defined in redis db then we assume this is very first - * start of syncd later on we can add additional checks here. - * - * TODO: if we add more switches then we need lane maps per switch. - * TODO: we also need other way to check if this is first start - * - * We could use VIDCOUNTER also, but if something is defined in the DB then - * we assume this is not the first start. - * - * TODO we need to fix this, since when there will be queue, it will still think - * this is first run, let's query HIDDEN ? - */ - - bool firstRun = m_client->hasNoHiddenKeysDefined(); - - SWSS_LOG_NOTICE("First Run: %s", firstRun ? "True" : "False"); - - return firstRun; -} - -static void timerWatchdogCallback( - _In_ int64_t span) -{ - SWSS_LOG_ENTER(); - - SWSS_LOG_ERROR("main loop execution exceeded %ld ms", span/1000); -} - -void Syncd::run() -{ - SWSS_LOG_ENTER(); - - WarmRestartTable warmRestartTable("STATE_DB"); // TODO from config - - syncd_restart_type_t shutdownType = SYNCD_RESTART_TYPE_COLD; - - volatile bool runMainLoop = true; - - bool inShutdownWaitMode = false; - - std::shared_ptr s = std::make_shared(); - - try - { - onSyncdStart(m_commandLineOptions->m_startType == SAI_START_TYPE_WARM_BOOT); - - // create notifications processing thread after we create_switch to - // make sure, we have switch_id translated to VID before we start - // processing possible quick fdb notifications, and pointer for - // notification queue is created before we create switch - m_processor->startNotificationsProcessingThread(); - - for (auto& sw: m_switches) - { - m_mdioIpcServer->setSwitchId(sw.second->getRid()); - } - - m_mdioIpcServer->startMdioThread(); - - SWSS_LOG_NOTICE("syncd listening for events"); - - s->addSelectable(m_selectableChannel.get()); - s->addSelectable(m_restartQuery.get()); - s->addSelectable(m_flexCounter.get()); - s->addSelectable(m_flexCounterGroup.get()); - - SWSS_LOG_NOTICE("starting main loop"); - } - catch(const std::exception &e) - { - SWSS_LOG_ERROR("Runtime error during syncd init: %s", e.what()); - - sendShutdownRequestAfterException(); - - s = std::make_shared(); - - s->addSelectable(m_restartQuery.get()); - s->addSelectable(m_selectableChannel.get()); - - inShutdownWaitMode = true; - - SWSS_LOG_NOTICE("starting main loop, ONLY restart query"); - - if (m_commandLineOptions->m_disableExitSleep) - runMainLoop = false; - } - - m_timerWatchdog.setCallback(timerWatchdogCallback); - - while (runMainLoop) - { - try - { - swss::Selectable *sel = NULL; - - int result = s->select(&sel); - - if (sel == m_restartQuery.get()) - { - /* - * This is actual a bad design, since selectable may pick up - * multiple events from the queue, and after restart those - * events will be forgotten since they were consumed already and - * this may lead to forget populate object table which will - * lead to unable to find some objects. - */ - - SWSS_LOG_NOTICE("is asic queue empty: %d", m_selectableChannel->empty()); - - while (!m_selectableChannel->empty()) - { - processEvent(*m_selectableChannel.get()); - } - - SWSS_LOG_NOTICE("drained queue"); - - WatchdogScope ws(m_timerWatchdog, "restart query"); - - shutdownType = handleRestartQuery(*m_restartQuery); - - if (shutdownType != SYNCD_RESTART_TYPE_PRE_SHUTDOWN && shutdownType != SYNCD_RESTART_TYPE_PRE_EXPRESS_SHUTDOWN) - { - // break out the event handling loop to shutdown syncd - runMainLoop = false; - break; - } - - // Handle switch pre-shutdown and wait for the final shutdown - // event - - SWSS_LOG_TIMER("%s pre-shutdown", (shutdownType == SYNCD_RESTART_TYPE_PRE_SHUTDOWN) ? "warm" : "express"); - - m_manager->removeAllCounters(); - - sai_status_t status = setRestartWarmOnAllSwitches(true); - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_RESTART_WARM=true: %s for pre-shutdown", - sai_serialize_status(status).c_str()); - - shutdownType = SYNCD_RESTART_TYPE_COLD; - - warmRestartTable.setFlagFailed(); - continue; - } - - if (shutdownType == SYNCD_RESTART_TYPE_PRE_EXPRESS_SHUTDOWN) - { - SWSS_LOG_NOTICE("express boot, enable fast API pre-shutdown"); - status = setFastAPIEnableOnAllSwitches(); - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_FAST_API_ENABLE=true: %s for express pre-shutdown. Fall back to cold restart", - sai_serialize_status(status).c_str()); - - shutdownType = SYNCD_RESTART_TYPE_COLD; - - warmRestartTable.setFlagFailed(); - continue; - } - } - - status = setPreShutdownOnAllSwitches(); - - if (status == SAI_STATUS_SUCCESS) - { - warmRestartTable.setPreShutdown(true); - - s = std::make_shared(); // make sure previous select is destroyed - - s->addSelectable(m_restartQuery.get()); - - SWSS_LOG_NOTICE("switched to PRE_SHUTDOWN, from now on accepting only shutdown requests"); - } - else - { - SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_PRE_SHUTDOWN=true: %s", - sai_serialize_status(status).c_str()); - - warmRestartTable.setPreShutdown(false); - - // Restore cold shutdown. - - setRestartWarmOnAllSwitches(false); - } - } - else if (sel == m_flexCounter.get()) - { - processFlexCounterEvent(*(swss::ConsumerTable*)sel); - } - else if (sel == m_flexCounterGroup.get()) - { - processFlexCounterGroupEvent(*(swss::ConsumerTable*)sel); - } - else if (sel == m_selectableChannel.get()) - { - if (inShutdownWaitMode) - { - processEventInShutdownWaitMode(*m_selectableChannel.get()); - } - else - { - processEvent(*m_selectableChannel.get()); - } - } - else - { - SWSS_LOG_ERROR("select failed: %d", result); - } - } - catch(const std::exception &e) - { - SWSS_LOG_ERROR("Runtime error: %s - entering shutdown-wait mode", e.what()); - - sendShutdownRequestAfterException(); - - s = std::make_shared(); - - s->addSelectable(m_restartQuery.get()); - s->addSelectable(m_selectableChannel.get()); - - inShutdownWaitMode = true; - - if (m_commandLineOptions->m_disableExitSleep) - runMainLoop = false; - - // make sure that if second exception will arise, then we break the loop - m_commandLineOptions->m_disableExitSleep = true; - } - } - - WatchdogScope ws(m_timerWatchdog, "shutting down syncd"); - - if (shutdownType == SYNCD_RESTART_TYPE_WARM) - { - const char *warmBootWriteFile = profileGetValue(0, SAI_KEY_WARM_BOOT_WRITE_FILE); - - SWSS_LOG_NOTICE("using warmBootWriteFile: '%s'", warmBootWriteFile); - - if (warmBootWriteFile == NULL) - { - SWSS_LOG_WARN("user requested warm shutdown but warmBootWriteFile is not specified, forcing cold shutdown"); - - shutdownType = SYNCD_RESTART_TYPE_COLD; - warmRestartTable.setWarmShutdown(false); - } - else - { - SWSS_LOG_NOTICE("Warm Reboot requested, keeping data plane running"); - - sai_status_t status = setRestartWarmOnAllSwitches(true); - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to set SAI_SWITCH_ATTR_RESTART_WARM=true: %s, fall back to cold restart", - sai_serialize_status(status).c_str()); - - shutdownType = SYNCD_RESTART_TYPE_COLD; - - warmRestartTable.setFlagFailed(); - } - } - } - - if (shutdownType == SYNCD_RESTART_TYPE_FAST || shutdownType == SYNCD_RESTART_TYPE_WARM || shutdownType == SYNCD_RESTART_TYPE_EXPRESS) - { - setUninitDataPlaneOnRemovalOnAllSwitches(); - } - - m_manager->removeAllCounters(); - - m_mdioIpcServer->stopMdioThread(); - - sai_status_t status = removeAllSwitches(); - - // Stop notification thread after removing switch - m_processor->stopNotificationsProcessingThread(); - - if (shutdownType == SYNCD_RESTART_TYPE_WARM || shutdownType == SYNCD_RESTART_TYPE_EXPRESS) - { - warmRestartTable.setWarmShutdown(status == SAI_STATUS_SUCCESS); - } - - SWSS_LOG_NOTICE("calling api uninitialize"); - - status = m_vendorSai->apiUninitialize(); - - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("failed to uninitialize api: %s", sai_serialize_status(status).c_str()); - } - - SWSS_LOG_NOTICE("uninitialize finished"); -} - -syncd_restart_type_t Syncd::handleRestartQuery( - _In_ swss::NotificationConsumer &restartQuery) -{ - SWSS_LOG_ENTER(); - - std::string op; - std::string data; - std::vector values; - - restartQuery.pop(op, data, values); - - m_timerWatchdog.setEventData(op + ":" + data); - - SWSS_LOG_NOTICE("received %s switch shutdown event", op.c_str()); - - return RequestShutdownCommandLineOptions::stringToRestartType(op); -} diff --git a/syncd/Syncd.h b/syncd/Syncd.h index e378919ef9..d633c9196d 100644 --- a/syncd/Syncd.h +++ b/syncd/Syncd.h @@ -27,67 +27,9 @@ #include "swss/notificationconsumer.h" #include -#include -#include namespace syncd { - /** - * @brief Link event damping configuration and state per port - */ - struct LinkEventDampingPortState - { - // Configuration parameters - sai_redis_link_event_damping_algorithm_t algorithm; - sai_redis_link_event_damping_algo_aied_config_t aied_config; - - // Runtime state for AIED algorithm - uint32_t current_penalty; // Current penalty value - uint64_t last_transition_time_ms; // Timestamp of last transition (milliseconds) - uint64_t last_decay_time_ms; // Timestamp of last decay calculation (milliseconds) - uint64_t damping_start_time_ms; // When damping state started (milliseconds) - bool is_damping_active; // Whether link is currently in damped state - sai_port_oper_status_t physical_status; // Physical port status - sai_port_oper_status_t advertised_status; // Last advertised status (may differ due to damping) - sai_port_oper_status_t last_suppressed_status; // Last event suppressed while damping - bool pending_state_sync; // Flag to indicate state mismatch needs propagation - - // Counters for observability - uint64_t pre_damping_link_transitions; - uint64_t pre_damping_up_events; - uint64_t pre_damping_down_events; - uint64_t post_damping_up_events; - uint64_t post_damping_down_events; - uint64_t post_damping_link_transitions; - - // Constructor with defaults - LinkEventDampingPortState() - : algorithm(SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED), - current_penalty(0), - last_transition_time_ms(0), - last_decay_time_ms(0), - damping_start_time_ms(0), - is_damping_active(false), - physical_status(SAI_PORT_OPER_STATUS_UNKNOWN), - advertised_status(SAI_PORT_OPER_STATUS_UNKNOWN), - last_suppressed_status(SAI_PORT_OPER_STATUS_UNKNOWN), - pending_state_sync(false), - pre_damping_link_transitions(0), - pre_damping_up_events(0), - pre_damping_down_events(0), - post_damping_up_events(0), - post_damping_down_events(0), - post_damping_link_transitions(0) - { - // Initialize AIED config with defaults - aied_config.max_suppress_time = 0; - aied_config.suppress_threshold = 0; - aied_config.reuse_threshold = 0; - aied_config.decay_half_life = 0; - aied_config.flap_penalty = 0; - } - }; - class Syncd { private: @@ -277,81 +219,6 @@ namespace syncd _In_ const std::vector &values, _In_ bool fromAsicChannel=true); - sai_status_t processLinkEventDampingConfigSet( - _In_ const swss::KeyOpFieldsValuesTuple &kco); - - private: // link event damping helpers - - /** - * @brief Apply link event damping algorithm to a port state change - * @param portVid Virtual object ID of the port - * @param newStatus New operational status of the port - * @return true if notification should be suppressed, false if it should be propagated - */ - bool applyLinkEventDamping( - _In_ sai_object_id_t portVid, - _In_ sai_port_oper_status_t newStatus); - - /** - * @brief Apply AIED damping algorithm - * @param state Port damping state - * @param newStatus New operational status - * @param currentTimeMs Current time in milliseconds - * @return true if should suppress, false if should propagate - */ - bool applyAiedAlgorithm( - _In_ sai_object_id_t portVid, - _In_ LinkEventDampingPortState& state, - _In_ sai_port_oper_status_t newStatus, - _In_ uint64_t currentTimeMs); - - /** - * @brief Decay penalty based on time elapsed - * @param state Port damping state - * @param currentTimeMs Current time in milliseconds - */ - void decayPenalty( - _In_ LinkEventDampingPortState& state, - _In_ uint64_t currentTimeMs); - - /** - * @brief Get current time in milliseconds - * @return Current time in milliseconds since epoch - */ - uint64_t getCurrentTimeMs(); - - /** - * @brief Proactively check all damped ports and enforce max_suppress_time - * Called periodically by timer thread to ensure ports don't exceed max_suppress_time - * even when no new port events arrive - */ - void checkDampedPortsTimeout(); - - /** - * @brief Timer thread function for proactive damping timeout enforcement - * Runs periodically to check if any damped ports have exceeded max_suppress_time - */ - void dampingTimerThreadFunc(); - - /** - * @brief Start the damping timer thread - */ - void startDampingTimerThread(); - - /** - * @brief Stop the damping timer thread - */ - void stopDampingTimerThread(); - - /** - * @brief Write damping counters to STATE_DB for a specific port - * @param portVid Virtual object ID of the port - * @param state Port damping state containing counters - */ - void writeDampingCountersToStateDb( - _In_ sai_object_id_t portVid, - _In_ const LinkEventDampingPortState& state); - private: // process quad oid sai_status_t processOidCreate( @@ -528,9 +395,6 @@ namespace syncd void sendNotifyResponse( _In_ sai_status_t status); - void sendLinkEventDampingConfigResponse( - _In_ sai_status_t status); - private: // snoop get response oids void snoopGetResponse( @@ -683,46 +547,5 @@ namespace syncd TimerWatchdog m_timerWatchdog; std::set m_createdInInitView; - - /** - * @brief Link event damping configuration per port - * Key: Port VID, Value: Damping state and configuration - */ - std::map m_portLinkEventDampingStates; - - /** - * @brief Mutex to protect link event damping state - */ - std::mutex m_linkEventDampingMutex; - - /** - * @brief STATE_DB connection for writing damping counters - */ - std::shared_ptr m_dbState; - - /** - * @brief STATE_DB table for damping counters - */ - std::shared_ptr m_dampingCounterTable; - - /** - * @brief Timer thread for proactive damping timeout enforcement - */ - std::shared_ptr m_dampingTimerThread; - - /** - * @brief Flag to control damping timer thread execution - */ - bool m_runDampingTimerThread; - - /** - * @brief Condition variable for damping timer thread - */ - std::condition_variable m_dampingTimerCv; - - /** - * @brief Mutex for damping timer thread synchronization - */ - std::mutex m_dampingTimerMutex; }; } diff --git a/syncd/tests/Makefile.am b/syncd/tests/Makefile.am index f548d69c27..2630eecdc9 100644 --- a/syncd/tests/Makefile.am +++ b/syncd/tests/Makefile.am @@ -5,7 +5,7 @@ LDADD_GTEST = -L/usr/src/gtest -lgtest -lgtest_main bin_PROGRAMS = tests tests_SOURCES = \ - main.cpp TestSyncdBrcm.cpp TestSyncdMlnx.cpp TestSyncdNvdaBf.cpp TestSyncdLib.cpp TestSyncdLinkEventDamping.cpp TestDisabledRedisClient.cpp + main.cpp TestSyncdBrcm.cpp TestSyncdMlnx.cpp TestSyncdNvdaBf.cpp TestSyncdLib.cpp TestDisabledRedisClient.cpp tests_CXXFLAGS = \ $(DBGFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS_COMMON) tests_LDADD = \ diff --git a/syncd/tests/TestSyncdLinkEventDamping.cpp b/syncd/tests/TestSyncdLinkEventDamping.cpp deleted file mode 100644 index f955e82bf5..0000000000 --- a/syncd/tests/TestSyncdLinkEventDamping.cpp +++ /dev/null @@ -1,255 +0,0 @@ -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include "swss/select.h" - -#include "Sai.h" -#include "Syncd.h" -#include "MetadataLogger.h" - -#include "TestSyncdLib.h" - -#include "meta/sai_serialize.h" -#include "sairediscommon.h" -#include "meta/RedisSelectableChannel.h" - -using namespace syncd; - -static const char* profile_get_value( - _In_ sai_switch_profile_id_t profile_id, - _In_ const char* variable) -{ - SWSS_LOG_ENTER(); - - return NULL; -} - -static int profile_get_next_value( - _In_ sai_switch_profile_id_t profile_id, - _Out_ const char** variable, - _Out_ const char** value) -{ - SWSS_LOG_ENTER(); - - if (value == NULL) - { - SWSS_LOG_INFO("resetting profile map iterator"); - return 0; - } - - if (variable == NULL) - { - SWSS_LOG_WARN("variable is null"); - return -1; - } - - SWSS_LOG_INFO("iterator reached end"); - return -1; -} - -static sai_service_method_table_t test_services = { - profile_get_value, - profile_get_next_value -}; - -void syncdLinkEventDampingWorkerThread() -{ - SWSS_LOG_ENTER(); - - swss::Logger::getInstance().setMinPrio(swss::Logger::SWSS_NOTICE); - MetadataLogger::initialize(); - - auto vendorSai = std::make_shared(); - auto commandLineOptions = std::make_shared(); - auto isWarmStart = false; - - commandLineOptions->m_enableSyncMode= true; - commandLineOptions->m_enableTempView = true; - commandLineOptions->m_disableExitSleep = true; - commandLineOptions->m_enableUnittests = true; - commandLineOptions->m_enableSaiBulkSupport = true; - commandLineOptions->m_startType = SAI_START_TYPE_COLD_BOOT; - commandLineOptions->m_redisCommunicationMode = SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC; - commandLineOptions->m_profileMapFile = "./brcm/testprofile.ini"; - - auto syncd = std::make_shared(vendorSai, commandLineOptions, isWarmStart); - syncd->run(); - - SWSS_LOG_NOTICE("Started syncd worker."); -} - -class LinkEventDampingTest : public ::testing::Test -{ -public: - LinkEventDampingTest() - { - SWSS_LOG_ENTER(); - - auto dbAsic = std::make_shared("ASIC_DB", 0); - - m_selectableChannel = std::make_shared( - dbAsic, - REDIS_TABLE_GETRESPONSE, - ASIC_STATE_TABLE, - TEMP_PREFIX, - false); - } - - virtual ~LinkEventDampingTest() = default; - -public: - virtual void SetUp() override - { - SWSS_LOG_ENTER(); - - m_switchId = SAI_NULL_OBJECT_ID; - - // flush ASIC DB - flushAsicDb(); - - syncdStart(); - createSwitch(); - } - - void syncdStart() - { - SWSS_LOG_ENTER(); - - // start syncd worker - m_worker = std::make_shared(syncdLinkEventDampingWorkerThread); - - // initialize SAI redis - m_sairedis = std::make_shared(); - - auto status = m_sairedis->apiInitialize(0, &test_services); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - // set communication mode - sai_attribute_t attr; - - attr.id = SAI_REDIS_SWITCH_ATTR_REDIS_COMMUNICATION_MODE; - attr.value.s32 = SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC; - - status = m_sairedis->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - // enable recording - attr.id = SAI_REDIS_SWITCH_ATTR_RECORD; - attr.value.booldata = true; - - status = m_sairedis->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - } - - void createSwitch() - { - SWSS_LOG_ENTER(); - - sai_attribute_t attr; - - // init view - attr.id = SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD; - attr.value.s32 = SAI_REDIS_NOTIFY_SYNCD_INIT_VIEW; - - auto status = m_sairedis->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - // apply view - attr.id = SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD; - attr.value.s32 = SAI_REDIS_NOTIFY_SYNCD_APPLY_VIEW; - - status = m_sairedis->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - // create switch - attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; - attr.value.booldata = true; - - status = m_sairedis->create(SAI_OBJECT_TYPE_SWITCH, &m_switchId, SAI_NULL_OBJECT_ID, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - } - - virtual void TearDown() override - { - SWSS_LOG_ENTER(); - - // uninitialize SAI redis - - auto status = m_sairedis->apiUninitialize(); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - // stop syncd worker - sendSyncdShutdownNotification(); - m_worker->join(); - } - -protected: - std::shared_ptr m_worker; - std::shared_ptr m_sairedis; - sai_object_id_t m_switchId; - std::shared_ptr m_selectableChannel; -}; - -sai_status_t getResponseStatus( - _In_ const std::string& command, - _In_ sairedis::RedisSelectableChannel *selectable, - _In_ bool init_view_mode) -{ - SWSS_LOG_ENTER(); - - swss::Select s; - s.addSelectable(selectable); - - while (true) - { - swss::Selectable *sel; - int result = s.select(&sel, 1000); - - if (result == swss::Select::OBJECT) - { - swss::KeyOpFieldsValuesTuple kco; - selectable->pop(kco, init_view_mode); - - const std::string &op = kfvOp(kco); - const std::string &opkey = kfvKey(kco); - - if (op != command) - { - SWSS_LOG_WARN("got not expected response: %s:%s", opkey.c_str(), op.c_str()); - continue; - } - - sai_status_t status; - sai_deserialize_status(opkey, status); - - return status; - } - - SWSS_LOG_ERROR("SELECT operation result: %s on %s", swss::Select::resultToString(result).c_str(), command.c_str()); - break; - } - - return SAI_STATUS_FAILURE; -} - -TEST_F(LinkEventDampingTest, SetLinkEventDampingConfigNotImplemented) -{ - std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + sai_serialize_object_id(SAI_NULL_OBJECT_ID); - - std::string str_attr_id = sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); - - std::string str_attr_value = sai_serialize_redis_link_event_damping_algorithm(SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); - - m_selectableChannel->set(key, {swss::FieldValueTuple(str_attr_id, str_attr_value)}, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - - EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, m_selectableChannel.get(), false), SAI_STATUS_NOT_IMPLEMENTED); -} diff --git a/unittest/lib/TestClientServerSai.cpp b/unittest/lib/TestClientServerSai.cpp index 27e24ebd97..3eb65733a6 100644 --- a/unittest/lib/TestClientServerSai.cpp +++ b/unittest/lib/TestClientServerSai.cpp @@ -146,74 +146,6 @@ TEST(ClientServerSai, logSet) EXPECT_EQ(SAI_STATUS_SUCCESS, css->logSet(SAI_API_PORT, SAI_LOG_LEVEL_NOTICE)); } -TEST(ClientServerSai, VerifySaiRedisPortAttrNotSupportedInClientMode) -{ - auto css = std::make_shared(); - - // Initialize as sairedis client. - EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_client_services)); - - sai_attribute_t attr; - attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; - attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED; - - EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); -} - -TEST(ClientServerSai, SetLinkEventDampingAlgorithm) -{ - auto css = std::make_shared(); - - // Initialize as sairedis server. - EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); - - sai_attribute_t attr; - attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; - attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED; - - EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); -} - -TEST(ClientServerSai, SetLinkEventDampingConfig) -{ - auto css = std::make_shared(); - - // Initialize as sairedis server. - EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); - - // Failure when config is NULL. - sai_attribute_t attr; - attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG; - attr.value.ptr = nullptr; - - EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); - - sai_redis_link_event_damping_algo_aied_config_t config = { - .max_suppress_time = 5000, - .suppress_threshold = 1500, - .reuse_threshold = 1200, - .decay_half_life = 3000, - .flap_penalty = 1000}; - - attr.value.ptr = (void *) &config; - - EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); -} - -TEST(ClientServerSai, SetInvalidSaiRedisPortAttribute) -{ - auto css = std::make_shared(); - - // Initialize as sairedis server. - EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); - - sai_attribute_t attr; - // Set an id that is not supported yet. - attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG + 100; - - EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); -} - TEST(ClientServerSai, bulkGetClearStats) { auto css = std::make_shared(); From 93fdb1ae0ebf9444cfac53b8300577246d21078e Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Fri, 22 May 2026 15:12:23 +0530 Subject: [PATCH 03/35] Adding link event damping support Signed-off-by: Sivakumar Thirukkanna Thevar --- lib/ClientSai.cpp | 3 +- lib/RedisRemoteSaiInterface.cpp | 96 +++ lib/RedisRemoteSaiInterface.h | 19 + lib/Sai.cpp | 6 + lib/sairediscommon.h | 2 + syncd/NotificationProcessor.cpp | 46 +- syncd/NotificationProcessor.h | 9 +- syncd/Syncd.cpp | 906 +++++++++++++++++++++- syncd/Syncd.h | 193 +++++ syncd/tests/Makefile.am | 2 +- syncd/tests/TestSyncdLinkEventDamping.cpp | 255 ++++++ unittest/lib/TestClientServerSai.cpp | 68 ++ 12 files changed, 1593 insertions(+), 12 deletions(-) create mode 100644 syncd/tests/TestSyncdLinkEventDamping.cpp diff --git a/lib/ClientSai.cpp b/lib/ClientSai.cpp index 63bc2392e2..06829109b8 100644 --- a/lib/ClientSai.cpp +++ b/lib/ClientSai.cpp @@ -240,7 +240,8 @@ sai_status_t ClientSai::set( SWSS_LOG_ENTER(); REDIS_CHECK_API_INITIALIZED(); - if (RedisRemoteSaiInterface::isRedisAttribute(objectType, attr)) + if (RedisRemoteSaiInterface::isRedisAttribute(objectType, attr) || + RedisRemoteSaiInterface::isRedisPortAttribute(objectType, attr)) { SWSS_LOG_ERROR("sairedis extension attributes are not supported in CLIENT mode"); diff --git a/lib/RedisRemoteSaiInterface.cpp b/lib/RedisRemoteSaiInterface.cpp index dabd7ad0b0..ada0201110 100644 --- a/lib/RedisRemoteSaiInterface.cpp +++ b/lib/RedisRemoteSaiInterface.cpp @@ -532,6 +532,83 @@ sai_status_t RedisRemoteSaiInterface::setRedisExtensionAttribute( return SAI_STATUS_FAILURE; } +sai_status_t RedisRemoteSaiInterface::setLinkEventDampingConfig( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const std::vector &values) +{ + SWSS_LOG_ENTER(); + + std::string key = sai_serialize_object_type(objectType) + ":" + sai_serialize_object_id(objectId); + + m_communicationChannel->set(key, values, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + if (m_syncMode) + { + swss::KeyOpFieldsValuesTuple kco; + auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, kco); + + m_recorder->recordGenericSetResponse(status); + + return status; + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t RedisRemoteSaiInterface::setRedisPortExtensionAttribute( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr) +{ + SWSS_LOG_ENTER(); + + if (attr == nullptr) + { + SWSS_LOG_ERROR("attr pointer is null"); + + return SAI_STATUS_INVALID_PARAMETER; + } + + std::string str_attr_id = sai_serialize_redis_port_attr_id( + static_cast(attr->id)); + + switch (attr->id) + { + case SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM: + { + std::string str_attr_value = sai_serialize_redis_link_event_damping_algorithm( + static_cast(attr->value.s32)); + + return setLinkEventDampingConfig( + objectType, objectId, {swss::FieldValueTuple(str_attr_id, str_attr_value)}); + } + case SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG: + { + sai_redis_link_event_damping_algo_aied_config_t *config = + (sai_redis_link_event_damping_algo_aied_config_t *)attr->value.ptr; + + if (config == NULL) + { + SWSS_LOG_ERROR("invalid link damping config attr value NULL"); + + return SAI_STATUS_INVALID_PARAMETER; + } + + std::string str_attr_value = sai_serialize_redis_link_event_damping_aied_config(*config); + + return setLinkEventDampingConfig( + objectType, objectId, {swss::FieldValueTuple(str_attr_id, str_attr_value)}); + } + default: + break; + } + + SWSS_LOG_ERROR("unknown redis port extension attribute: %d", attr->id); + + return SAI_STATUS_INVALID_PARAMETER; +} + bool RedisRemoteSaiInterface::isSaiS8ListValidString( _In_ const sai_s8_list_t &s8list) { @@ -666,6 +743,11 @@ sai_status_t RedisRemoteSaiInterface::set( return setRedisExtensionAttribute(objectType, objectId, attr); } + if (RedisRemoteSaiInterface::isRedisPortAttribute(objectType, attr)) + { + return setRedisPortExtensionAttribute(objectType, objectId, attr); + } + auto status = set( objectType, sai_serialize_object_id(objectId), @@ -2201,6 +2283,20 @@ bool RedisRemoteSaiInterface::isRedisAttribute( return true; } +bool RedisRemoteSaiInterface::isRedisPortAttribute( + _In_ sai_object_id_t objectType, + _In_ const sai_attribute_t* attr) +{ + SWSS_LOG_ENTER(); + + if ((objectType != SAI_OBJECT_TYPE_PORT) || (attr == nullptr) || (attr->id < SAI_PORT_ATTR_CUSTOM_RANGE_START)) + { + return false; + } + + return true; +} + void RedisRemoteSaiInterface::handleNotification( _In_ const std::string &name, _In_ const std::string &serializedNotification, diff --git a/lib/RedisRemoteSaiInterface.h b/lib/RedisRemoteSaiInterface.h index 74019eccf8..20ffe4a6ef 100644 --- a/lib/RedisRemoteSaiInterface.h +++ b/lib/RedisRemoteSaiInterface.h @@ -231,6 +231,15 @@ namespace sairedis _In_ sai_object_id_t switchId, _In_ const sai_attribute_t* attr); + /** + * @brief Checks whether attribute is custom SAI_REDIS_PORT attribute. + * + * This function should only be used on port_api set function. + */ + static bool isRedisPortAttribute( + _In_ sai_object_id_t obejctType, + _In_ const sai_attribute_t* attr); + void setMeta( _In_ std::weak_ptr meta); @@ -401,6 +410,11 @@ namespace sairedis _In_ sai_object_id_t objectId, _In_ const sai_attribute_t *attr); + sai_status_t setRedisPortExtensionAttribute( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr); + bool isSaiS8ListValidString( _In_ const sai_s8_list_t &s8list); @@ -428,6 +442,11 @@ namespace sairedis _In_ sai_object_id_t switchId, _In_ const sai_attribute_t *attr); + sai_status_t setLinkEventDampingConfig( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const std::vector &values); + void clear_local_state(); sai_switch_notifications_t processNotification( diff --git a/lib/Sai.cpp b/lib/Sai.cpp index 22b33e4764..03f441e560 100644 --- a/lib/Sai.cpp +++ b/lib/Sai.cpp @@ -251,6 +251,12 @@ sai_status_t Sai::set( REDIS_CHECK_CONTEXT(objectId); + if (RedisRemoteSaiInterface::isRedisPortAttribute(objectType, attr)) + { + // skip metadata if attribute is redis extension port attribute. + return context->m_redisSai->set(objectType, objectId, attr); + } + return context->m_meta->set(objectType, objectId, attr); } diff --git a/lib/sairediscommon.h b/lib/sairediscommon.h index 4594cab5b0..c2da2a08e5 100644 --- a/lib/sairediscommon.h +++ b/lib/sairediscommon.h @@ -52,6 +52,8 @@ #define REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_QUERY "object_type_get_availability_query" #define REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_RESPONSE "object_type_get_availability_response" +#define REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET "link_event_damping_config_set" + #define REDIS_FLEX_COUNTER_COMMAND_START_POLL "start_poll" #define REDIS_FLEX_COUNTER_COMMAND_STOP_POLL "stop_poll" #define REDIS_FLEX_COUNTER_COMMAND_SET_GROUP "set_counter_group" diff --git a/syncd/NotificationProcessor.cpp b/syncd/NotificationProcessor.cpp index 1bd5ce0b97..20eb3937cb 100644 --- a/syncd/NotificationProcessor.cpp +++ b/syncd/NotificationProcessor.cpp @@ -18,8 +18,10 @@ using namespace saimeta; NotificationProcessor::NotificationProcessor( _In_ std::shared_ptr producer, _In_ std::shared_ptr client, - _In_ std::function synchronizer): + _In_ std::function synchronizer, + _In_ std::function linkEventDampingApplier): m_synchronizer(synchronizer), + m_linkEventDampingApplier(linkEventDampingApplier), m_client(client), m_notifications(producer) { @@ -494,6 +496,9 @@ void NotificationProcessor::process_on_port_state_change( SWSS_LOG_DEBUG("port notification count: %u", count); + // Vector to store filtered notifications (after damping applied) + std::vector filtered_notifications; + for (uint32_t i = 0; i < count; i++) { sai_port_oper_status_notification_t *oper_stat = &data[i]; @@ -520,14 +525,43 @@ void NotificationProcessor::process_on_port_state_change( * Port may be in process of removal. OA may receive notification for VID either * SAI_NULL_OBJECT_ID or non exist at time of processing */ + SWSS_LOG_INFO("Port VID %s state change notification: %s", + sai_serialize_object_id(oper_stat->port_id).c_str(), + sai_serialize_port_oper_status(oper_stat->port_state).c_str()); - SWSS_LOG_INFO("Port VID %s state change notification", - sai_serialize_object_id(oper_stat->port_id).c_str()); - } + // Apply link event damping if configured + bool should_suppress = false; + if (m_linkEventDampingApplier != nullptr && oper_stat->port_id != SAI_NULL_OBJECT_ID) + { + should_suppress = m_linkEventDampingApplier(oper_stat->port_id, oper_stat->port_state); + } - std::string s = sai_serialize_port_oper_status_ntf(count, data); + if (!should_suppress) + { + // Add to filtered notifications + filtered_notifications.push_back(*oper_stat); + SWSS_LOG_INFO("Port state change PROPAGATED: %s -> %s", + sai_serialize_object_id(oper_stat->port_id).c_str(), + sai_serialize_port_oper_status(oper_stat->port_state).c_str()); + } + else + { + SWSS_LOG_INFO("Port state change SUPPRESSED by damping: %s -> %s", + sai_serialize_object_id(oper_stat->port_id).c_str(), + sai_serialize_port_oper_status(oper_stat->port_state).c_str()); + } + } - sendNotification(SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE, s); + // Send only non-suppressed (filtered) notifications + if (!filtered_notifications.empty()) + { + std::string s = sai_serialize_port_oper_status_ntf((uint32_t)filtered_notifications.size(), filtered_notifications.data()); + sendNotification(SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE, s); + } + else + { + SWSS_LOG_DEBUG("All port state changes were suppressed by damping, no notification sent"); + } } void NotificationProcessor::process_on_bfd_session_state_change( diff --git a/syncd/NotificationProcessor.h b/syncd/NotificationProcessor.h index 3ee4a941cf..4d17748663 100644 --- a/syncd/NotificationProcessor.h +++ b/syncd/NotificationProcessor.h @@ -22,7 +22,8 @@ namespace syncd NotificationProcessor( _In_ std::shared_ptr producer, _In_ std::shared_ptr client, - _In_ std::function synchronizer); + _In_ std::function synchronizer, + _In_ std::function linkEventDampingApplier = nullptr); virtual ~NotificationProcessor(); @@ -211,6 +212,12 @@ namespace syncd std::function m_synchronizer; + /** + * @brief Callback function to apply link event damping to port state changes + * Returns true if notification should be suppressed, false if it should be propagated + */ + std::function m_linkEventDampingApplier; + std::shared_ptr m_client; std::shared_ptr m_notifications; diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index d2bc0ba056..97c7e79add 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -67,7 +67,8 @@ Syncd::Syncd( m_vendorSai(vendorSai), m_veryFirstRun(false), m_enableSyncMode(false), - m_timerWatchdog(cmd->m_watchdogWarnTimeSpan * WD_DELAY_FACTOR) + m_timerWatchdog(cmd->m_watchdogWarnTimeSpan * WD_DELAY_FACTOR), + m_runDampingTimerThread(false) { SWSS_LOG_ENTER(); @@ -134,6 +135,8 @@ Syncd::Syncd( // we need STATE_DB ASIC_DB and COUNTERS_DB m_dbAsic = std::make_shared(m_contextConfig->m_dbAsic, 0); + m_dbState = std::make_shared("STATE_DB", 0); + m_dampingCounterTable = std::make_shared(m_dbState.get(), "LINK_EVENT_DAMPING_STATS"); m_mdioIpcServer = std::make_shared(m_vendorSai, m_commandLineOptions->m_globalContext); if (m_contextConfig->m_zmqEnable) @@ -179,7 +182,11 @@ Syncd::Syncd( m_client = std::make_shared(m_dbAsic); } - m_processor = std::make_shared(m_notifications, m_client, std::bind(&Syncd::syncProcessNotification, this, _1)); + m_processor = std::make_shared( + m_notifications, + m_client, + std::bind(&Syncd::syncProcessNotification, this, _1), + std::bind(&Syncd::applyLinkEventDamping, this, _1, _2)); m_handler = std::make_shared(m_processor); m_sn.onFdbEvent = std::bind(&NotificationHandler::onFdbEvent, m_handler.get(), _1, _2); @@ -263,6 +270,9 @@ Syncd::Syncd( m_breakConfig = BreakConfigParser::parseBreakConfig(m_commandLineOptions->m_breakConfig); + // Start the damping timer thread for proactive timeout enforcement + startDampingTimerThread(); + SWSS_LOG_NOTICE("syncd started"); } @@ -270,7 +280,8 @@ Syncd::~Syncd() { SWSS_LOG_ENTER(); - // empty + // Stop the damping timer thread + stopDampingTimerThread(); } void Syncd::performStartupLogic() @@ -473,6 +484,9 @@ sai_status_t Syncd::processSingleEvent( if (op == REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_QUERY) return processObjectTypeGetAvailabilityQuery(kco); + if (op == REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET) + return processLinkEventDampingConfigSet(kco); + if (op == REDIS_FLEX_COUNTER_COMMAND_START_POLL) return processFlexCounterEvent(key, SET_COMMAND, kfvFieldsValues(kco)); @@ -842,6 +856,892 @@ sai_status_t Syncd::processStatsStCapabilityQuery( return status; } +sai_status_t Syncd::processLinkEventDampingConfigSet( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + auto& key = kfvKey(kco); + auto& values = kfvFieldsValues(kco); + + // Parse the key format: "OBJECT_TYPE:OBJECT_ID" + size_t colon_pos = key.find(":"); + if (colon_pos == std::string::npos) + { + SWSS_LOG_ERROR("invalid key format: %s", key.c_str()); + sendLinkEventDampingConfigResponse(SAI_STATUS_INVALID_PARAMETER); + return SAI_STATUS_INVALID_PARAMETER; + } + + // Extract object type and object ID + std::string strObjectType = key.substr(0, colon_pos); + std::string strObjectId = key.substr(colon_pos + 1); + + sai_object_type_t objectType; + sai_deserialize_object_type(strObjectType, objectType); + + // Link event damping is a software-based feature - validate port exists + if (objectType != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_ERROR("invalid object type for link event damping config: %s", + strObjectType.c_str()); + sendLinkEventDampingConfigResponse(SAI_STATUS_INVALID_PARAMETER); + return SAI_STATUS_INVALID_PARAMETER; + } + + sai_object_id_t portVid; + sai_deserialize_object_id(strObjectId, portVid); + + // Validate that the port exists by translating VID to RID + sai_object_id_t portRid = m_translator->translateVidToRid(portVid); + + if (portRid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("failed to translate port VID to RID"); + sendLinkEventDampingConfigResponse(SAI_STATUS_INVALID_PARAMETER); + return SAI_STATUS_INVALID_PARAMETER; + } + + // Link event damping is a software-based feature implemented in syncd. + // Store the configuration parameters on the port object so that + // OnPortStateChange can apply the damping algorithm before forwarding notifications. + // The damping parameters will be used to decide whether to suppress link state changes. + sai_status_t status = SAI_STATUS_SUCCESS; + + // Acquire lock to protect damping state + std::lock_guard lock(m_linkEventDampingMutex); + + // Get or create damping state for this port + auto& dampingState = m_portLinkEventDampingStates[portVid]; + + // Process each attribute and apply it to the port + for (const auto& v : values) + { + std::string strAttrId = fvField(v); + std::string strAttrValue = fvValue(v); + + SWSS_LOG_DEBUG("processing link event damping attribute: %s = %s", + strAttrId.c_str(), strAttrValue.c_str()); + + // Deserialize attribute ID + sai_redis_port_attr_t attrId; + sai_deserialize_redis_port_attr_id(strAttrId, attrId); + + // Parse and set the attribute value based on the attribute ID + switch (attrId) + { + case SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM: + { + sai_redis_link_event_damping_algorithm_t algo; + sai_deserialize_redis_link_event_damping_algorithm(strAttrValue, algo); + + SWSS_LOG_INFO("setting link event damping algorithm on port %s: %d", + strObjectId.c_str(), algo); + + // Link event damping is a software-only feature as of now + // Store the configuration locally for use in notification + // processing. + dampingState.algorithm = algo; + + status = SAI_STATUS_SUCCESS; + break; + } + + case SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG: + { + // Allocate temporary memory for the config structure + sai_redis_link_event_damping_algo_aied_config_t *config = + new sai_redis_link_event_damping_algo_aied_config_t(); + + sai_deserialize_redis_link_event_damping_aied_config(strAttrValue, *config); + + SWSS_LOG_INFO("setting link event damping AIED config on port %s: " + "max_suppress_time=%u, suppress_threshold=%u, " + "reuse_threshold=%u, decay_half_life=%u, flap_penalty=%u", + strObjectId.c_str(), config->max_suppress_time, + config->suppress_threshold, config->reuse_threshold, + config->decay_half_life, config->flap_penalty); + + // Link event damping is a software-only feature as of now + // Store the configuration locally for use in notification + // processing. + dampingState.aied_config = *config; + + // Free the temporary allocated memory + delete config; + + status = SAI_STATUS_SUCCESS; + break; + } + + default: + { + SWSS_LOG_WARN("unknown attribute ID: %d for link event damping", attrId); + status = SAI_STATUS_INVALID_PARAMETER; + break; + } + } + + if (status != SAI_STATUS_SUCCESS && status != SAI_STATUS_INVALID_PARAMETER) + { + // Log error but continue processing other attributes + SWSS_LOG_WARN("error processing link event damping attribute %s: %s", + strAttrId.c_str(), sai_serialize_status(status).c_str()); + break; + } + } + + sendLinkEventDampingConfigResponse(status); + + return status; +} + +void Syncd::sendLinkEventDampingConfigResponse( + _In_ sai_status_t status) +{ + SWSS_LOG_ENTER(); + + // If sync mode is not enabled, do not send response. + if (!m_enableSyncMode) + { + return; + } + + std::string strStatus = sai_serialize_status(status); + + std::vector entry; + + SWSS_LOG_INFO("sending link event damping config response: %s", strStatus.c_str()); + + m_selectableChannel->set(strStatus, entry, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); +} + +uint64_t Syncd::getCurrentTimeMs() +{ + auto now = std::chrono::system_clock::now(); + auto duration = now.time_since_epoch(); + return std::chrono::duration_cast(duration).count(); +} + +void Syncd::decayPenalty( + _In_ LinkEventDampingPortState& state, + _In_ uint64_t currentTimeMs) +{ + SWSS_LOG_ENTER(); + + if (state.current_penalty == 0) + { + return; // No penalty to decay + } + + if (state.aied_config.decay_half_life == 0) + { + return; // Invalid configuration, skip decay + } + + // Use last_decay_time to track decay independently from state transitions + // This ensures penalty decays even if no link state changes occur + uint64_t base_time = state.last_decay_time_ms; + if (base_time == 0) + { + // First time calculating decay - use last transition time as base + base_time = state.last_transition_time_ms; + } + + // Calculate elapsed time in milliseconds since last decay + uint64_t elapsed_ms = currentTimeMs - base_time; + + if (elapsed_ms <= 0) + { + return; // No time has elapsed + } + + // Penalty decay formula: P(t) = P0 * (0.5 ^ (t / half_life)) + // We use floating point for the calculation + double half_lives = (double)elapsed_ms / state.aied_config.decay_half_life; + double decay_factor = std::pow(0.5, half_lives); + uint32_t decayed_penalty = (uint32_t)(state.current_penalty * decay_factor); + + // Ensure penalty doesn't go below 0 + if (decayed_penalty < state.current_penalty) + { + state.current_penalty = decayed_penalty; + state.last_decay_time_ms = currentTimeMs; // Update last decay time + SWSS_LOG_DEBUG("Port penalty decayed: %u (half_life=%u ms, elapsed=%lu ms, decay_factor=%f)", + state.current_penalty, state.aied_config.decay_half_life, elapsed_ms, decay_factor); + } + else if (state.last_decay_time_ms == 0) + { + // Initialize decay time on first check + state.last_decay_time_ms = currentTimeMs; + } +} + +bool Syncd::applyAiedAlgorithm( + _In_ sai_object_id_t portVid, + _In_ LinkEventDampingPortState& state, + _In_ sai_port_oper_status_t newStatus, + _In_ uint64_t currentTimeMs) +{ + SWSS_LOG_ENTER(); + + std::string portVidStr = sai_serialize_object_id(portVid); + + // Validate configuration + if (state.aied_config.decay_half_life > state.aied_config.max_suppress_time) + { + SWSS_LOG_WARN("Port VID %s invalid damping configuration: " + "decay_half_life (%u ms) > max_suppress_time (%u ms). Damping disabled.", + portVidStr.c_str(), state.aied_config.decay_half_life, + state.aied_config.max_suppress_time); + return false; // Damping disabled for invalid config + } + + // First, apply penalty decay + decayPenalty(state, currentTimeMs); + + // Track if damping was active before this event + bool was_damping_active_before = state.is_damping_active; + + // Check if a link state change occurred + if (state.physical_status != newStatus) + { + // Link state transitioned + state.pre_damping_link_transitions++; + + if (newStatus == SAI_PORT_OPER_STATUS_UP) + { + state.pre_damping_up_events++; + } + else if (newStatus == SAI_PORT_OPER_STATUS_DOWN) + { + state.pre_damping_down_events++; + // Reset damping timer on DOWN event if damping is already active + if (state.is_damping_active) + { + state.damping_start_time_ms = currentTimeMs; + SWSS_LOG_DEBUG("Damping timer reset on DOWN event: new start time = %lu ms", + currentTimeMs); + } + } + + // Add penalty ONLY on DOWN events (UP -> DOWN) + if (state.physical_status == SAI_PORT_OPER_STATUS_UP && newStatus == SAI_PORT_OPER_STATUS_DOWN) + { + state.current_penalty += state.aied_config.flap_penalty; + + // Calculate penalty ceiling: 2^(max_suppress_time/decay_half_life) * reuse_threshold + double exponent = (double)state.aied_config.max_suppress_time / state.aied_config.decay_half_life; + uint32_t penalty_ceiling = (uint32_t)(std::pow(2.0, exponent) * state.aied_config.reuse_threshold); + + if (state.current_penalty > penalty_ceiling) + { + state.current_penalty = penalty_ceiling; + } + + SWSS_LOG_DEBUG("Port DOWN event: penalty accumulated to %u " + "(penalty_ceiling: %u, flap_penalty: %u)", + state.current_penalty, penalty_ceiling, state.aied_config.flap_penalty); + } + else + { + SWSS_LOG_DEBUG("Port UP event: no penalty added (penalty remains: %u)", + state.current_penalty); + } + + // Update physical status and timestamps + state.physical_status = newStatus; + state.last_transition_time_ms = currentTimeMs; + + // If this is the first state change after damping config was set, + // initialize decay time as well + if (state.last_decay_time_ms == 0) + { + state.last_decay_time_ms = currentTimeMs; + } + + // Check if we should enter damping state + if (state.current_penalty >= state.aied_config.suppress_threshold && + !state.is_damping_active) + { + std::string portVidStr = sai_serialize_object_id(portVid); + SWSS_LOG_NOTICE("Port VID %s entering damped state: penalty (%u) >= " + "suppress_threshold (%u) at time %lu ms. Current event will be " + "PROPAGATED, future events will be suppressed.", + portVidStr.c_str(), state.current_penalty, + state.aied_config.suppress_threshold, currentTimeMs); + state.is_damping_active = true; + state.damping_start_time_ms = currentTimeMs; + } + + // Write updated pre-damping counters and physical status to STATE_DB + writeDampingCountersToStateDb(portVid, state); + } + + // Damping exits when EITHER: + // 1. Time-based: damping_duration_ms >= max_suppress_time + // 2. Penalty-based: current_penalty < reuse_threshold (decay-based recovery) + if (state.is_damping_active) + { + // Check timeout - never suppress longer than max_suppress_time + // This is a hard timestamp-based limit to prevent infinite suppression + uint64_t damping_duration_ms = currentTimeMs - state.damping_start_time_ms; + + if (damping_duration_ms >= state.aied_config.max_suppress_time) + { + // Store temporary strings to avoid dangling pointers + std::string physicalStatusStr = sai_serialize_port_oper_status(state.physical_status); + std::string advertisedStatusStr = sai_serialize_port_oper_status(state.advertised_status); + SWSS_LOG_NOTICE("Port VID %s exiting damped state: max suppress time (%u ms) " + "exceeded. Duration: %lu ms. Physical state: %s, Advertised state: %s", + portVidStr.c_str(), state.aied_config.max_suppress_time, + damping_duration_ms, physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + state.is_damping_active = false; + state.damping_start_time_ms = 0; // Reset timer when exiting damping + + // Propagate last link event when penalty decays below reuse threshold + if (state.advertised_status != state.physical_status) + { + state.pending_state_sync = true; + SWSS_LOG_NOTICE("Port VID %s state mismatch detected on damping " + "exit (timeout): physical=%s, advertised=%s. " + "Marking for state sync on next notification.", + portVidStr.c_str(), physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + } + state.advertised_status = state.physical_status; + + // Write updated state to STATE_DB after exiting damping + writeDampingCountersToStateDb(portVid, state); + } + // Check reuse threshold - exit if penalty decays below threshold + // Penalty decays based on last_decay_time tracking + else if (state.current_penalty < state.aied_config.reuse_threshold) + { + // Store temporary strings to avoid dangling pointers + std::string physicalStatusStr = sai_serialize_port_oper_status(state.physical_status); + std::string advertisedStatusStr = sai_serialize_port_oper_status(state.advertised_status); + SWSS_LOG_NOTICE("Port VID %s exiting damped state: penalty (%u) < " + "reuse_threshold (%u). Penalty decayed due to exponential decay " + "formula. Physical state: %s, Advertised state: %s", + portVidStr.c_str(), state.current_penalty, + state.aied_config.reuse_threshold, physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + state.is_damping_active = false; + state.damping_start_time_ms = 0; // Reset timer when exiting damping + + // Propagate last link event when penalty decays below reuse threshold + if (state.advertised_status != state.physical_status) + { + state.pending_state_sync = true; + SWSS_LOG_NOTICE("Port VID %s state mismatch detected on damping " + "exit (decay): physical=%s, advertised=%s. " + "Marking for state sync on next notification.", + portVidStr.c_str(), physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + } + state.advertised_status = state.physical_status; + + // Write updated state to STATE_DB after exiting damping + writeDampingCountersToStateDb(portVid, state); + } + } + + // Determine if notification should be suppressed + bool should_suppress = false; + + // Only suppress when damping is active AND this is NOT the threshold-crossing event + // The threshold-crossing event itself should be propagated + if (state.is_damping_active && was_damping_active_before) + { + // Calculate current suppression time based on damping algorithm + uint64_t damping_duration_ms = currentTimeMs - state.damping_start_time_ms; + + if (damping_duration_ms < state.aied_config.max_suppress_time) + { + // Calculate expected suppression time + // suppression_time = decay_half_life * log2(reuse_threshold / accumulated_penalty) + if (state.current_penalty > 0 && state.current_penalty >= state.aied_config.reuse_threshold) + { + double suppression_ratio = (double)state.aied_config.reuse_threshold / state.current_penalty; + double expected_suppress_time = state.aied_config.decay_half_life * std::log2(suppression_ratio); + + SWSS_LOG_DEBUG("Port damping active: suppression_time=%.0f ms, " + "max_suppress_time=%u ms, current_penalty=%u, reuse_threshold=%u", + expected_suppress_time, state.aied_config.max_suppress_time, + state.current_penalty, state.aied_config.reuse_threshold); + } + + // Suppress the notification + should_suppress = true; + state.last_suppressed_status = newStatus; // Track what was suppressed + // Store temporary strings to avoid dangling pointers + std::string newStatusStr = sai_serialize_port_oper_status(newStatus); + SWSS_LOG_NOTICE("Port VID %s suppressing port state change notification: " + "new_status=%s (damping active, penalty: %u, duration: %lu ms)", + portVidStr.c_str(), newStatusStr.c_str(), + state.current_penalty, damping_duration_ms); + } + else + { + // Should not happen due to exit check above, but handle gracefully + should_suppress = false; + + SWSS_LOG_WARN("Port VID %s unexpected state: damping_active=true but " + "duration >= max_suppress_time", portVidStr.c_str()); + } + } + else + { + // Damping is NOT active OR this is the threshold-crossing event - propagate the notification + should_suppress = false; + std::string physicalStatusStr = sai_serialize_port_oper_status(state.physical_status); + std::string advertisedStatusStr = sai_serialize_port_oper_status(state.advertised_status); + + // Track advertised transitions + if (newStatus == SAI_PORT_OPER_STATUS_UP) + { + state.post_damping_up_events++; + } + else if (newStatus == SAI_PORT_OPER_STATUS_DOWN) + { + state.post_damping_down_events++; + } + state.post_damping_link_transitions++; + + // SYNC STATE: Update advertised to match new state + state.advertised_status = newStatus; + + // Clear the pending sync flag since state is now synchronized + if (state.pending_state_sync && newStatus == state.physical_status) + { + state.pending_state_sync = false; + SWSS_LOG_INFO("Port state sync completed: physical=%s, advertised=%s", + physicalStatusStr.c_str(), advertisedStatusStr.c_str()); + } + + // Write updated counters to STATE_DB + writeDampingCountersToStateDb(portVid, state); + + // When damping changes state (enabled->disabled), propagate the event + if (was_damping_active_before && !state.is_damping_active) + { + SWSS_LOG_NOTICE("Port VID %s state change PROPAGATED (damping state changed " + "from active to inactive): physical=%s, advertised=%s", + portVidStr.c_str(), physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + } + else + { + SWSS_LOG_INFO("Port VID %s state change PROPAGATED: physical=%s, advertised=%s (damping inactive)", + portVidStr.c_str(), physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + } + + } + + return should_suppress; +} + +bool Syncd::applyLinkEventDamping( + _In_ sai_object_id_t portVid, + _In_ sai_port_oper_status_t newStatus) +{ + SWSS_LOG_ENTER(); + + std::lock_guard lock(m_linkEventDampingMutex); + + // Check if damping is configured for this port + auto it = m_portLinkEventDampingStates.find(portVid); + if (it == m_portLinkEventDampingStates.end()) + { + // No damping configured for this port + return false; // Don't suppress + } + + LinkEventDampingPortState& state = it->second; + + // Check if damping algorithm is enabled + if (state.algorithm == SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED) + { + return false; // Damping disabled + } + + uint64_t currentTimeMs = getCurrentTimeMs(); + + // Apply the appropriate damping algorithm + switch (state.algorithm) + { + case SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED: + return applyAiedAlgorithm(portVid, state, newStatus, currentTimeMs); + + default: + SWSS_LOG_WARN("Unknown damping algorithm: %d", state.algorithm); + return false; + } +} + +void Syncd::checkDampedPortsTimeout() +{ + SWSS_LOG_ENTER(); + + std::lock_guard lock(m_linkEventDampingMutex); + + uint64_t currentTimeMs = getCurrentTimeMs(); + std::vector> portsToSync; + + // Iterate through all ports with damping configured + for (auto& kv : m_portLinkEventDampingStates) + { + auto& portVid = kv.first; + auto& state = kv.second; + + // Only check ports that are currently in damped state + if (!state.is_damping_active) + { + continue; + } + + // Check if damping algorithm is enabled + if (state.algorithm != SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED) + { + continue; + } + + std::string portVidStr = sai_serialize_object_id(portVid); + std::string physicalStatusStr = sai_serialize_port_oper_status(state.physical_status); + std::string advertisedStatusStr = sai_serialize_port_oper_status(state.advertised_status); + // Apply penalty decay first - penalty naturally decays over time + decayPenalty(state, currentTimeMs); + + // Check if penalty has decayed below reuse threshold + if (state.current_penalty < state.aied_config.reuse_threshold) + { + SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s exiting damped state: " + "penalty (%u) < reuse_threshold (%u). Penalty decayed due to " + "exponential decay formula. Physical state: %s, Advertised state: %s", + portVidStr.c_str(), state.current_penalty, + state.aied_config.reuse_threshold, physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + + // Exit damping state + state.is_damping_active = false; + state.damping_start_time_ms = 0; // Reset timer when exiting damping + + // Check if there's a state mismatch that needs to be propagated + if (state.advertised_status != state.physical_status) + { + SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s state mismatch " + "detected on damping exit (decay): " + "physical=%s, advertised=%s. Will send notification.", + portVidStr.c_str(), physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + + // Update advertised status to match physical + state.advertised_status = state.physical_status; + state.pending_state_sync = false; + + // Collect port info for notification + portsToSync.push_back(std::make_pair(portVid, state.physical_status)); + } + else + { + SWSS_LOG_INFO("Proactive timeout check: Port VID %s exited damping " + "with no state mismatch.", portVidStr.c_str()); + } + + // Write updated state to STATE_DB after exiting damping + writeDampingCountersToStateDb(portVid, state); + + // Skip to next port since we've already handled this one + continue; + } + + // Calculate how long the port has been damped + uint64_t damping_duration_ms = currentTimeMs - state.damping_start_time_ms; + + // Check if max_suppress_time has been exceeded + if (damping_duration_ms >= state.aied_config.max_suppress_time) + { + SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s exiting damped " + "state: max suppress time (%u ms) exceeded. " + "Duration: %lu ms. Physical state: %s, Advertised state: %s", + portVidStr.c_str(), state.aied_config.max_suppress_time, + damping_duration_ms, physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + + // Exit damping state + state.is_damping_active = false; + state.damping_start_time_ms = 0; // Reset timer when exiting damping + + // Check if there's a state mismatch that needs to be propagated + if (state.advertised_status != state.physical_status) + { + SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s state mismatch detected on damping exit: " + "physical=%s, advertised=%s. Will send notification.", + portVidStr.c_str(), physicalStatusStr.c_str(), + advertisedStatusStr.c_str()); + + // Update advertised status to match physical + state.advertised_status = state.physical_status; + state.pending_state_sync = false; + + // Collect port info for notification + portsToSync.push_back(std::make_pair(portVid, state.physical_status)); + } + else + { + SWSS_LOG_INFO("Proactive timeout check: Port VID %s exited damping " + "with no state mismatch.", portVidStr.c_str()); + } + + // Write updated state to STATE_DB after exiting damping + writeDampingCountersToStateDb(portVid, state); + } + else + { + // Port is still in damped state - write updated stats to STATE_DB + // to reflect the decayed penalty value in real-time + writeDampingCountersToStateDb(portVid, state); + + SWSS_LOG_DEBUG("Proactive timeout check: Port VID %s still damped: " + "penalty=%u, duration=%lu ms", portVidStr.c_str(), + state.current_penalty, damping_duration_ms); + } + } + + // Release the lock before sending notifications + // Note: We make a copy of the port list above to avoid holding the lock during notification send + // Send notifications for ports that need state synchronization + if (!portsToSync.empty()) + { + SWSS_LOG_NOTICE("Proactive timeout check: Sending %zu port state notifications " + "after damping timeout", portsToSync.size()); + + // Send each port notification through the notification system + for (const auto& kv : portsToSync) + { + const auto& portVid = kv.first; + const auto& status = kv.second; + + std::string portVidStr = sai_serialize_object_id(portVid); + std::string statusStr = sai_serialize_port_oper_status(status); + + // Build notification data + sai_port_oper_status_notification_t notification; + notification.port_id = portVid; + notification.port_state = status; + + std::string serialized = sai_serialize_port_oper_status_ntf(1, ¬ification); + + // Send directly through the notification producer + std::vector entry; + m_notifications->send(SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE, + serialized, entry); + SWSS_LOG_NOTICE("Proactive timeout check: Sent notification for Port VID %s -> %s", + portVidStr.c_str(), statusStr.c_str()); + } + } +} + +void Syncd::dampingTimerThreadFunc() +{ + SWSS_LOG_ENTER(); + SWSS_LOG_NOTICE("Damping timer thread started"); + + while (true) + { + { + std::unique_lock lock(m_dampingTimerMutex); + + // Wait for 1 second or until signaled to stop + if (m_dampingTimerCv.wait_for(lock, std::chrono::seconds(1), [this] { return !m_runDampingTimerThread; })) + { + // Signaled to stop + SWSS_LOG_NOTICE("Damping timer thread received stop signal"); + break; + } + + // Check if still running (in case of spurious wakeup) + if (!m_runDampingTimerThread) + { + break; + } + } + + // Perform the proactive timeout check + try + { + checkDampedPortsTimeout(); + } + catch (const std::exception& e) + { + SWSS_LOG_ERROR("Exception in damping timer thread: %s", e.what()); + } + catch (...) + { + SWSS_LOG_ERROR("Unknown exception in damping timer thread"); + } + } + + SWSS_LOG_NOTICE("Damping timer thread stopped"); +} + +void Syncd::startDampingTimerThread() +{ + SWSS_LOG_ENTER(); + + if (m_runDampingTimerThread) + { + SWSS_LOG_WARN("Damping timer thread already running"); + return; + } + + m_runDampingTimerThread = true; + m_dampingTimerThread = std::make_shared(&Syncd::dampingTimerThreadFunc, this); + + SWSS_LOG_NOTICE("Started damping timer thread for proactive max_suppress_time enforcement"); +} + +void Syncd::stopDampingTimerThread() +{ + SWSS_LOG_ENTER(); + + if (!m_runDampingTimerThread) + { + SWSS_LOG_INFO("Damping timer thread not running"); + return; + } + + // Signal the thread to stop + { + std::lock_guard lock(m_dampingTimerMutex); + m_runDampingTimerThread = false; + } + m_dampingTimerCv.notify_one(); + + // Wait for the thread to finish + if (m_dampingTimerThread && m_dampingTimerThread->joinable()) + { + m_dampingTimerThread->join(); + SWSS_LOG_NOTICE("Damping timer thread stopped and joined"); + } + + m_dampingTimerThread.reset(); +} + +void Syncd::writeDampingCountersToStateDb( + _In_ sai_object_id_t portVid, + _In_ const LinkEventDampingPortState& state) +{ + SWSS_LOG_ENTER(); + + // Convert VID to string for STATE_DB key + std::string portVidStr = sai_serialize_object_id(portVid); + + // Prepare counter fields + std::vector fields; + fields.emplace_back("pre_damping_link_transitions", std::to_string(state.pre_damping_link_transitions)); + fields.emplace_back("pre_damping_up_events", std::to_string(state.pre_damping_up_events)); + fields.emplace_back("pre_damping_down_events", std::to_string(state.pre_damping_down_events)); + fields.emplace_back("post_damping_up_events", std::to_string(state.post_damping_up_events)); + fields.emplace_back("post_damping_down_events", std::to_string(state.post_damping_down_events)); + fields.emplace_back("post_damping_link_transitions", std::to_string(state.post_damping_link_transitions)); + + // Add damping state information + fields.emplace_back("is_damping_active", state.is_damping_active ? "true" : "false"); + fields.emplace_back("current_penalty", std::to_string(state.current_penalty)); + fields.emplace_back("damping_start_time_ms", std::to_string(state.damping_start_time_ms)); + fields.emplace_back("physical_status", sai_serialize_port_oper_status(state.physical_status)); + fields.emplace_back("advertised_status", sai_serialize_port_oper_status(state.advertised_status)); + + // Write to STATE_DB + m_dampingCounterTable->set(portVidStr, fields); + + SWSS_LOG_DEBUG("Wrote damping counters to STATE_DB for port %s", portVidStr.c_str()); +} + +sai_status_t Syncd::clearDampingCounters( + _In_ sai_object_id_t portVid) +{ + SWSS_LOG_ENTER(); + + std::lock_guard lock(m_linkEventDampingMutex); + + auto it = m_portLinkEventDampingStates.find(portVid); + if (it == m_portLinkEventDampingStates.end()) + { + SWSS_LOG_WARN("Port VID %s not found in damping states", sai_serialize_object_id(portVid).c_str()); + return SAI_STATUS_ITEM_NOT_FOUND; + } + + // Reset all counters to zero + it->second.pre_damping_link_transitions = 0; + it->second.pre_damping_up_events = 0; + it->second.pre_damping_down_events = 0; + it->second.post_damping_up_events = 0; + it->second.post_damping_down_events = 0; + it->second.post_damping_link_transitions = 0; + + // Write updated (zeroed) counters to STATE_DB + writeDampingCountersToStateDb(portVid, it->second); + + SWSS_LOG_NOTICE("Cleared damping counters for port %s", sai_serialize_object_id(portVid).c_str()); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t Syncd::processLinkEventDampingCounterClear( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + const std::string& key = kfvKey(kco); + const std::string& op = kfvOp(kco); + + SWSS_LOG_INFO("Processing link event damping counter clear: key=%s, op=%s", key.c_str(), op.c_str()); + + // Parse the key format: "OBJECT_TYPE:OBJECT_ID" + size_t colonPos = key.find(':'); + if (colonPos == std::string::npos) + { + SWSS_LOG_ERROR("invalid key format for counter clear: %s", key.c_str()); + return SAI_STATUS_INVALID_PARAMETER; + } + + std::string strObjectType = key.substr(0, colonPos); + std::string strObjectId = key.substr(colonPos + 1); + + sai_object_type_t objectType; + sai_deserialize_object_type(strObjectType, objectType); + + if (objectType != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_ERROR("invalid object type for counter clear: %s", strObjectType.c_str()); + return SAI_STATUS_INVALID_PARAMETER; + } + + // Get port VID + sai_object_id_t portVid; + sai_deserialize_object_id(strObjectId, portVid); + + // Clear counters + sai_status_t status = clearDampingCounters(portVid); + + // Send response back (similar to config set response) + std::string responseKey = "REDIS_ASIC_STATE_COMMAND_COUNTER_CLEAR_RESPONSE"; + std::vector responseFields; + responseFields.emplace_back("status", sai_serialize_status(status)); + responseFields.emplace_back("key", key); + + m_selectableChannel->set(responseKey, responseFields, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); + + SWSS_LOG_INFO("sending link event damping counter clear response: %s", sai_serialize_status(status).c_str()); + + return status; +} + sai_status_t Syncd::processFdbFlush( _In_ const swss::KeyOpFieldsValuesTuple &kco) { diff --git a/syncd/Syncd.h b/syncd/Syncd.h index d633c9196d..00b2a13d00 100644 --- a/syncd/Syncd.h +++ b/syncd/Syncd.h @@ -27,9 +27,67 @@ #include "swss/notificationconsumer.h" #include +#include +#include namespace syncd { + /** + * @brief Link event damping configuration and state per port + */ + struct LinkEventDampingPortState + { + // Configuration parameters + sai_redis_link_event_damping_algorithm_t algorithm; + sai_redis_link_event_damping_algo_aied_config_t aied_config; + + // Runtime state for AIED algorithm + uint32_t current_penalty; // Current penalty value + uint64_t last_transition_time_ms; // Timestamp of last transition (milliseconds) + uint64_t last_decay_time_ms; // Timestamp of last decay calculation (milliseconds) + uint64_t damping_start_time_ms; // When damping state started (milliseconds) + bool is_damping_active; // Whether link is currently in damped state + sai_port_oper_status_t physical_status; // Physical port status + sai_port_oper_status_t advertised_status; // Last advertised status (may differ due to damping) + sai_port_oper_status_t last_suppressed_status; // Last event suppressed while damping + bool pending_state_sync; // Flag to indicate state mismatch needs propagation + + // Counters for observability + uint64_t pre_damping_link_transitions; + uint64_t pre_damping_up_events; + uint64_t pre_damping_down_events; + uint64_t post_damping_up_events; + uint64_t post_damping_down_events; + uint64_t post_damping_link_transitions; + + // Constructor with defaults + LinkEventDampingPortState() + : algorithm(SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED), + current_penalty(0), + last_transition_time_ms(0), + last_decay_time_ms(0), + damping_start_time_ms(0), + is_damping_active(false), + physical_status(SAI_PORT_OPER_STATUS_UNKNOWN), + advertised_status(SAI_PORT_OPER_STATUS_UNKNOWN), + last_suppressed_status(SAI_PORT_OPER_STATUS_UNKNOWN), + pending_state_sync(false), + pre_damping_link_transitions(0), + pre_damping_up_events(0), + pre_damping_down_events(0), + post_damping_up_events(0), + post_damping_down_events(0), + post_damping_link_transitions(0) + { + // Initialize AIED config with defaults + aied_config.max_suppress_time = 0; + aied_config.suppress_threshold = 0; + aied_config.reuse_threshold = 0; + aied_config.decay_half_life = 0; + aied_config.flap_penalty = 0; + } + }; + class Syncd { private: @@ -219,6 +277,97 @@ namespace syncd _In_ const std::vector &values, _In_ bool fromAsicChannel=true); + sai_status_t processLinkEventDampingConfigSet( + _In_ const swss::KeyOpFieldsValuesTuple &kco); + + private: // link event damping helpers + + /** + * @brief Apply link event damping algorithm to a port state change + * @param portVid Virtual object ID of the port + * @param newStatus New operational status of the port + * @return true if notification should be suppressed, false if it should be propagated + */ + bool applyLinkEventDamping( + _In_ sai_object_id_t portVid, + _In_ sai_port_oper_status_t newStatus); + + /** + * @brief Apply AIED damping algorithm + * @param state Port damping state + * @param newStatus New operational status + * @param currentTimeMs Current time in milliseconds + * @return true if should suppress, false if should propagate + */ + bool applyAiedAlgorithm( + _In_ sai_object_id_t portVid, + _In_ LinkEventDampingPortState& state, + _In_ sai_port_oper_status_t newStatus, + _In_ uint64_t currentTimeMs); + + /** + * @brief Decay penalty based on time elapsed + * @param state Port damping state + * @param currentTimeMs Current time in milliseconds + */ + void decayPenalty( + _In_ LinkEventDampingPortState& state, + _In_ uint64_t currentTimeMs); + + /** + * @brief Get current time in milliseconds + * @return Current time in milliseconds since epoch + */ + uint64_t getCurrentTimeMs(); + + /** + * @brief Proactively check all damped ports and enforce max_suppress_time + * Called periodically by timer thread to ensure ports don't exceed max_suppress_time + * even when no new port events arrive + */ + void checkDampedPortsTimeout(); + + /** + * @brief Timer thread function for proactive damping timeout enforcement + * Runs periodically to check if any damped ports have exceeded max_suppress_time + */ + void dampingTimerThreadFunc(); + + /** + * @brief Start the damping timer thread + */ + void startDampingTimerThread(); + + /** + * @brief Stop the damping timer thread + */ + void stopDampingTimerThread(); + + /** + * @brief Write damping counters to STATE_DB for a specific port + * @param portVid Virtual object ID of the port + * @param state Port damping state containing counters + */ + void writeDampingCountersToStateDb( + _In_ sai_object_id_t portVid, + _In_ const LinkEventDampingPortState& state); + + /** + * @brief Clear/reset damping counters for a specific port + * @param portVid Virtual object ID of the port + * @return SAI_STATUS_SUCCESS on success + */ + sai_status_t clearDampingCounters( + _In_ sai_object_id_t portVid); + + /** + * @brief Process damping counter clear command + * @param kco Key-operation-fields tuple from Redis + * @return SAI_STATUS_SUCCESS on success + */ + sai_status_t processLinkEventDampingCounterClear( + _In_ const swss::KeyOpFieldsValuesTuple &kco); + private: // process quad oid sai_status_t processOidCreate( @@ -395,6 +544,9 @@ namespace syncd void sendNotifyResponse( _In_ sai_status_t status); + void sendLinkEventDampingConfigResponse( + _In_ sai_status_t status); + private: // snoop get response oids void snoopGetResponse( @@ -547,5 +699,46 @@ namespace syncd TimerWatchdog m_timerWatchdog; std::set m_createdInInitView; + + /** + * @brief Link event damping configuration per port + * Key: Port VID, Value: Damping state and configuration + */ + std::map m_portLinkEventDampingStates; + + /** + * @brief Mutex to protect link event damping state + */ + std::mutex m_linkEventDampingMutex; + + /** + * @brief STATE_DB connection for writing damping counters + */ + std::shared_ptr m_dbState; + + /** + * @brief STATE_DB table for damping counters + */ + std::shared_ptr m_dampingCounterTable; + + /** + * @brief Timer thread for proactive damping timeout enforcement + */ + std::shared_ptr m_dampingTimerThread; + + /** + * @brief Flag to control damping timer thread execution + */ + bool m_runDampingTimerThread; + + /** + * @brief Condition variable for damping timer thread + */ + std::condition_variable m_dampingTimerCv; + + /** + * @brief Mutex for damping timer thread synchronization + */ + std::mutex m_dampingTimerMutex; }; } diff --git a/syncd/tests/Makefile.am b/syncd/tests/Makefile.am index 2630eecdc9..f548d69c27 100644 --- a/syncd/tests/Makefile.am +++ b/syncd/tests/Makefile.am @@ -5,7 +5,7 @@ LDADD_GTEST = -L/usr/src/gtest -lgtest -lgtest_main bin_PROGRAMS = tests tests_SOURCES = \ - main.cpp TestSyncdBrcm.cpp TestSyncdMlnx.cpp TestSyncdNvdaBf.cpp TestSyncdLib.cpp TestDisabledRedisClient.cpp + main.cpp TestSyncdBrcm.cpp TestSyncdMlnx.cpp TestSyncdNvdaBf.cpp TestSyncdLib.cpp TestSyncdLinkEventDamping.cpp TestDisabledRedisClient.cpp tests_CXXFLAGS = \ $(DBGFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS_COMMON) tests_LDADD = \ diff --git a/syncd/tests/TestSyncdLinkEventDamping.cpp b/syncd/tests/TestSyncdLinkEventDamping.cpp new file mode 100644 index 0000000000..f955e82bf5 --- /dev/null +++ b/syncd/tests/TestSyncdLinkEventDamping.cpp @@ -0,0 +1,255 @@ +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include "swss/select.h" + +#include "Sai.h" +#include "Syncd.h" +#include "MetadataLogger.h" + +#include "TestSyncdLib.h" + +#include "meta/sai_serialize.h" +#include "sairediscommon.h" +#include "meta/RedisSelectableChannel.h" + +using namespace syncd; + +static const char* profile_get_value( + _In_ sai_switch_profile_id_t profile_id, + _In_ const char* variable) +{ + SWSS_LOG_ENTER(); + + return NULL; +} + +static int profile_get_next_value( + _In_ sai_switch_profile_id_t profile_id, + _Out_ const char** variable, + _Out_ const char** value) +{ + SWSS_LOG_ENTER(); + + if (value == NULL) + { + SWSS_LOG_INFO("resetting profile map iterator"); + return 0; + } + + if (variable == NULL) + { + SWSS_LOG_WARN("variable is null"); + return -1; + } + + SWSS_LOG_INFO("iterator reached end"); + return -1; +} + +static sai_service_method_table_t test_services = { + profile_get_value, + profile_get_next_value +}; + +void syncdLinkEventDampingWorkerThread() +{ + SWSS_LOG_ENTER(); + + swss::Logger::getInstance().setMinPrio(swss::Logger::SWSS_NOTICE); + MetadataLogger::initialize(); + + auto vendorSai = std::make_shared(); + auto commandLineOptions = std::make_shared(); + auto isWarmStart = false; + + commandLineOptions->m_enableSyncMode= true; + commandLineOptions->m_enableTempView = true; + commandLineOptions->m_disableExitSleep = true; + commandLineOptions->m_enableUnittests = true; + commandLineOptions->m_enableSaiBulkSupport = true; + commandLineOptions->m_startType = SAI_START_TYPE_COLD_BOOT; + commandLineOptions->m_redisCommunicationMode = SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC; + commandLineOptions->m_profileMapFile = "./brcm/testprofile.ini"; + + auto syncd = std::make_shared(vendorSai, commandLineOptions, isWarmStart); + syncd->run(); + + SWSS_LOG_NOTICE("Started syncd worker."); +} + +class LinkEventDampingTest : public ::testing::Test +{ +public: + LinkEventDampingTest() + { + SWSS_LOG_ENTER(); + + auto dbAsic = std::make_shared("ASIC_DB", 0); + + m_selectableChannel = std::make_shared( + dbAsic, + REDIS_TABLE_GETRESPONSE, + ASIC_STATE_TABLE, + TEMP_PREFIX, + false); + } + + virtual ~LinkEventDampingTest() = default; + +public: + virtual void SetUp() override + { + SWSS_LOG_ENTER(); + + m_switchId = SAI_NULL_OBJECT_ID; + + // flush ASIC DB + flushAsicDb(); + + syncdStart(); + createSwitch(); + } + + void syncdStart() + { + SWSS_LOG_ENTER(); + + // start syncd worker + m_worker = std::make_shared(syncdLinkEventDampingWorkerThread); + + // initialize SAI redis + m_sairedis = std::make_shared(); + + auto status = m_sairedis->apiInitialize(0, &test_services); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // set communication mode + sai_attribute_t attr; + + attr.id = SAI_REDIS_SWITCH_ATTR_REDIS_COMMUNICATION_MODE; + attr.value.s32 = SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC; + + status = m_sairedis->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // enable recording + attr.id = SAI_REDIS_SWITCH_ATTR_RECORD; + attr.value.booldata = true; + + status = m_sairedis->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + } + + void createSwitch() + { + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + + // init view + attr.id = SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD; + attr.value.s32 = SAI_REDIS_NOTIFY_SYNCD_INIT_VIEW; + + auto status = m_sairedis->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // apply view + attr.id = SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD; + attr.value.s32 = SAI_REDIS_NOTIFY_SYNCD_APPLY_VIEW; + + status = m_sairedis->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // create switch + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = true; + + status = m_sairedis->create(SAI_OBJECT_TYPE_SWITCH, &m_switchId, SAI_NULL_OBJECT_ID, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + } + + virtual void TearDown() override + { + SWSS_LOG_ENTER(); + + // uninitialize SAI redis + + auto status = m_sairedis->apiUninitialize(); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // stop syncd worker + sendSyncdShutdownNotification(); + m_worker->join(); + } + +protected: + std::shared_ptr m_worker; + std::shared_ptr m_sairedis; + sai_object_id_t m_switchId; + std::shared_ptr m_selectableChannel; +}; + +sai_status_t getResponseStatus( + _In_ const std::string& command, + _In_ sairedis::RedisSelectableChannel *selectable, + _In_ bool init_view_mode) +{ + SWSS_LOG_ENTER(); + + swss::Select s; + s.addSelectable(selectable); + + while (true) + { + swss::Selectable *sel; + int result = s.select(&sel, 1000); + + if (result == swss::Select::OBJECT) + { + swss::KeyOpFieldsValuesTuple kco; + selectable->pop(kco, init_view_mode); + + const std::string &op = kfvOp(kco); + const std::string &opkey = kfvKey(kco); + + if (op != command) + { + SWSS_LOG_WARN("got not expected response: %s:%s", opkey.c_str(), op.c_str()); + continue; + } + + sai_status_t status; + sai_deserialize_status(opkey, status); + + return status; + } + + SWSS_LOG_ERROR("SELECT operation result: %s on %s", swss::Select::resultToString(result).c_str(), command.c_str()); + break; + } + + return SAI_STATUS_FAILURE; +} + +TEST_F(LinkEventDampingTest, SetLinkEventDampingConfigNotImplemented) +{ + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + sai_serialize_object_id(SAI_NULL_OBJECT_ID); + + std::string str_attr_id = sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); + + std::string str_attr_value = sai_serialize_redis_link_event_damping_algorithm(SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + m_selectableChannel->set(key, {swss::FieldValueTuple(str_attr_id, str_attr_value)}, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, m_selectableChannel.get(), false), SAI_STATUS_NOT_IMPLEMENTED); +} diff --git a/unittest/lib/TestClientServerSai.cpp b/unittest/lib/TestClientServerSai.cpp index 3eb65733a6..27e24ebd97 100644 --- a/unittest/lib/TestClientServerSai.cpp +++ b/unittest/lib/TestClientServerSai.cpp @@ -146,6 +146,74 @@ TEST(ClientServerSai, logSet) EXPECT_EQ(SAI_STATUS_SUCCESS, css->logSet(SAI_API_PORT, SAI_LOG_LEVEL_NOTICE)); } +TEST(ClientServerSai, VerifySaiRedisPortAttrNotSupportedInClientMode) +{ + auto css = std::make_shared(); + + // Initialize as sairedis client. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_client_services)); + + sai_attribute_t attr; + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; + attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED; + + EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); +} + +TEST(ClientServerSai, SetLinkEventDampingAlgorithm) +{ + auto css = std::make_shared(); + + // Initialize as sairedis server. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); + + sai_attribute_t attr; + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; + attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED; + + EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); +} + +TEST(ClientServerSai, SetLinkEventDampingConfig) +{ + auto css = std::make_shared(); + + // Initialize as sairedis server. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); + + // Failure when config is NULL. + sai_attribute_t attr; + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG; + attr.value.ptr = nullptr; + + EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); + + sai_redis_link_event_damping_algo_aied_config_t config = { + .max_suppress_time = 5000, + .suppress_threshold = 1500, + .reuse_threshold = 1200, + .decay_half_life = 3000, + .flap_penalty = 1000}; + + attr.value.ptr = (void *) &config; + + EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); +} + +TEST(ClientServerSai, SetInvalidSaiRedisPortAttribute) +{ + auto css = std::make_shared(); + + // Initialize as sairedis server. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); + + sai_attribute_t attr; + // Set an id that is not supported yet. + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG + 100; + + EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); +} + TEST(ClientServerSai, bulkGetClearStats) { auto css = std::make_shared(); From 87532d6994f5c49adf284e096b20e6aae2f92dc5 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Wed, 27 May 2026 10:27:03 +0530 Subject: [PATCH 04/35] Addressing review comments Signed-off-by: Sivakumar Thirukkanna Thevar --- lib/RedisRemoteSaiInterface.cpp | 2 +- lib/RedisRemoteSaiInterface.h | 2 +- syncd/Syncd.cpp | 114 +++------------- syncd/tests/TestSyncdLinkEventDamping.cpp | 154 +++++++++++++++++++++- 4 files changed, 168 insertions(+), 104 deletions(-) diff --git a/lib/RedisRemoteSaiInterface.cpp b/lib/RedisRemoteSaiInterface.cpp index ada0201110..0a05eca585 100644 --- a/lib/RedisRemoteSaiInterface.cpp +++ b/lib/RedisRemoteSaiInterface.cpp @@ -2289,7 +2289,7 @@ bool RedisRemoteSaiInterface::isRedisPortAttribute( { SWSS_LOG_ENTER(); - if ((objectType != SAI_OBJECT_TYPE_PORT) || (attr == nullptr) || (attr->id < SAI_PORT_ATTR_CUSTOM_RANGE_START)) + if ((objectType != SAI_OBJECT_TYPE_PORT) || (attr == nullptr) || (attr->id < SAI_PORT_ATTR_CUSTOM_RANGE_START) || (attr->id >= SAI_PORT_ATTR_EXTENSIONS_RANGE_BASE)) { return false; } diff --git a/lib/RedisRemoteSaiInterface.h b/lib/RedisRemoteSaiInterface.h index 20ffe4a6ef..85976cd8d2 100644 --- a/lib/RedisRemoteSaiInterface.h +++ b/lib/RedisRemoteSaiInterface.h @@ -237,7 +237,7 @@ namespace sairedis * This function should only be used on port_api set function. */ static bool isRedisPortAttribute( - _In_ sai_object_id_t obejctType, + _In_ sai_object_id_t objectType, _In_ const sai_attribute_t* attr); void setMeta( diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index 97c7e79add..1fcba6768c 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -40,6 +40,7 @@ #include #include +#include #define DEF_SAI_WARM_BOOT_DATA_FILE "/var/warmboot/sai-warmboot.bin" #define SAI_FAILURE_DUMP_SCRIPT "/usr/bin/sai_failure_dump.sh" @@ -893,9 +894,9 @@ sai_status_t Syncd::processLinkEventDampingConfigSet( sai_deserialize_object_id(strObjectId, portVid); // Validate that the port exists by translating VID to RID - sai_object_id_t portRid = m_translator->translateVidToRid(portVid); + sai_object_id_t portRid; - if (portRid == SAI_NULL_OBJECT_ID) + if (!m_translator->tryTranslateVidToRid(portVid, portRid)) { SWSS_LOG_ERROR("failed to translate port VID to RID"); sendLinkEventDampingConfigResponse(SAI_STATUS_INVALID_PARAMETER); @@ -950,25 +951,21 @@ sai_status_t Syncd::processLinkEventDampingConfigSet( case SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG: { // Allocate temporary memory for the config structure - sai_redis_link_event_damping_algo_aied_config_t *config = - new sai_redis_link_event_damping_algo_aied_config_t(); + sai_redis_link_event_damping_algo_aied_config_t config{}; - sai_deserialize_redis_link_event_damping_aied_config(strAttrValue, *config); + sai_deserialize_redis_link_event_damping_aied_config(strAttrValue, config); SWSS_LOG_INFO("setting link event damping AIED config on port %s: " "max_suppress_time=%u, suppress_threshold=%u, " "reuse_threshold=%u, decay_half_life=%u, flap_penalty=%u", - strObjectId.c_str(), config->max_suppress_time, - config->suppress_threshold, config->reuse_threshold, - config->decay_half_life, config->flap_penalty); + strObjectId.c_str(), config.max_suppress_time, + config.suppress_threshold, config.reuse_threshold, + config.decay_half_life, config.flap_penalty); // Link event damping is a software-only feature as of now // Store the configuration locally for use in notification // processing. - dampingState.aied_config = *config; - - // Free the temporary allocated memory - delete config; + dampingState.aied_config = config; status = SAI_STATUS_SUCCESS; break; @@ -1018,7 +1015,7 @@ void Syncd::sendLinkEventDampingConfigResponse( uint64_t Syncd::getCurrentTimeMs() { - auto now = std::chrono::system_clock::now(); + auto now = std::chrono::steady_clock::now(); auto duration = now.time_since_epoch(); return std::chrono::duration_cast(duration).count(); } @@ -1088,10 +1085,12 @@ bool Syncd::applyAiedAlgorithm( std::string portVidStr = sai_serialize_object_id(portVid); // Validate configuration - if (state.aied_config.decay_half_life > state.aied_config.max_suppress_time) + if (state.aied_config.decay_half_life != 0 || + state.aied_config.max_suppress_time != 0 || + state.aied_config.decay_half_life > state.aied_config.max_suppress_time) { SWSS_LOG_WARN("Port VID %s invalid damping configuration: " - "decay_half_life (%u ms) > max_suppress_time (%u ms). Damping disabled.", + "decay_half_life (%u ms) max_suppress_time (%u ms). Damping disabled.", portVidStr.c_str(), state.aied_config.decay_half_life, state.aied_config.max_suppress_time); return false; // Damping disabled for invalid config @@ -1164,7 +1163,6 @@ bool Syncd::applyAiedAlgorithm( if (state.current_penalty >= state.aied_config.suppress_threshold && !state.is_damping_active) { - std::string portVidStr = sai_serialize_object_id(portVid); SWSS_LOG_NOTICE("Port VID %s entering damped state: penalty (%u) >= " "suppress_threshold (%u) at time %lu ms. Current event will be " "PROPAGATED, future events will be suppressed.", @@ -1376,6 +1374,10 @@ bool Syncd::applyLinkEventDamping( case SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED: return applyAiedAlgorithm(portVid, state, newStatus, currentTimeMs); + case SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED: + SWSS_LOG_INFO("Damping algorithm disabled: %d", state.algorithm); + return false; + default: SWSS_LOG_WARN("Unknown damping algorithm: %d", state.algorithm); return false; @@ -1662,86 +1664,6 @@ void Syncd::writeDampingCountersToStateDb( SWSS_LOG_DEBUG("Wrote damping counters to STATE_DB for port %s", portVidStr.c_str()); } -sai_status_t Syncd::clearDampingCounters( - _In_ sai_object_id_t portVid) -{ - SWSS_LOG_ENTER(); - - std::lock_guard lock(m_linkEventDampingMutex); - - auto it = m_portLinkEventDampingStates.find(portVid); - if (it == m_portLinkEventDampingStates.end()) - { - SWSS_LOG_WARN("Port VID %s not found in damping states", sai_serialize_object_id(portVid).c_str()); - return SAI_STATUS_ITEM_NOT_FOUND; - } - - // Reset all counters to zero - it->second.pre_damping_link_transitions = 0; - it->second.pre_damping_up_events = 0; - it->second.pre_damping_down_events = 0; - it->second.post_damping_up_events = 0; - it->second.post_damping_down_events = 0; - it->second.post_damping_link_transitions = 0; - - // Write updated (zeroed) counters to STATE_DB - writeDampingCountersToStateDb(portVid, it->second); - - SWSS_LOG_NOTICE("Cleared damping counters for port %s", sai_serialize_object_id(portVid).c_str()); - - return SAI_STATUS_SUCCESS; -} - -sai_status_t Syncd::processLinkEventDampingCounterClear( - _In_ const swss::KeyOpFieldsValuesTuple &kco) -{ - SWSS_LOG_ENTER(); - - const std::string& key = kfvKey(kco); - const std::string& op = kfvOp(kco); - - SWSS_LOG_INFO("Processing link event damping counter clear: key=%s, op=%s", key.c_str(), op.c_str()); - - // Parse the key format: "OBJECT_TYPE:OBJECT_ID" - size_t colonPos = key.find(':'); - if (colonPos == std::string::npos) - { - SWSS_LOG_ERROR("invalid key format for counter clear: %s", key.c_str()); - return SAI_STATUS_INVALID_PARAMETER; - } - - std::string strObjectType = key.substr(0, colonPos); - std::string strObjectId = key.substr(colonPos + 1); - - sai_object_type_t objectType; - sai_deserialize_object_type(strObjectType, objectType); - - if (objectType != SAI_OBJECT_TYPE_PORT) - { - SWSS_LOG_ERROR("invalid object type for counter clear: %s", strObjectType.c_str()); - return SAI_STATUS_INVALID_PARAMETER; - } - - // Get port VID - sai_object_id_t portVid; - sai_deserialize_object_id(strObjectId, portVid); - - // Clear counters - sai_status_t status = clearDampingCounters(portVid); - - // Send response back (similar to config set response) - std::string responseKey = "REDIS_ASIC_STATE_COMMAND_COUNTER_CLEAR_RESPONSE"; - std::vector responseFields; - responseFields.emplace_back("status", sai_serialize_status(status)); - responseFields.emplace_back("key", key); - - m_selectableChannel->set(responseKey, responseFields, REDIS_ASIC_STATE_COMMAND_GETRESPONSE); - - SWSS_LOG_INFO("sending link event damping counter clear response: %s", sai_serialize_status(status).c_str()); - - return status; -} - sai_status_t Syncd::processFdbFlush( _In_ const swss::KeyOpFieldsValuesTuple &kco) { diff --git a/syncd/tests/TestSyncdLinkEventDamping.cpp b/syncd/tests/TestSyncdLinkEventDamping.cpp index f955e82bf5..c18b1991ea 100644 --- a/syncd/tests/TestSyncdLinkEventDamping.cpp +++ b/syncd/tests/TestSyncdLinkEventDamping.cpp @@ -241,15 +241,157 @@ sai_status_t getResponseStatus( return SAI_STATUS_FAILURE; } -TEST_F(LinkEventDampingTest, SetLinkEventDampingConfigNotImplemented) +TEST_F(LinkEventDampingTest, SetLinkEventDampingConfigInvalidPort) { - std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + sai_serialize_object_id(SAI_NULL_OBJECT_ID); + // SAI_NULL_OBJECT_ID is not a registered port VID, so VID→RID translation + // returns SAI_NULL_OBJECT_ID, which causes processLinkEventDampingConfigSet() + // to return SAI_STATUS_INVALID_PARAMETER + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + + sai_serialize_object_id(SAI_NULL_OBJECT_ID); - std::string str_attr_id = sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); + std::string str_attr_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); - std::string str_attr_value = sai_serialize_redis_link_event_damping_algorithm(SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + std::string str_attr_value = sai_serialize_redis_link_event_damping_algorithm( + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); - m_selectableChannel->set(key, {swss::FieldValueTuple(str_attr_id, str_attr_value)}, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + m_selectableChannel->set(key, + {swss::FieldValueTuple(str_attr_id, str_attr_value)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, m_selectableChannel.get(), false), SAI_STATUS_NOT_IMPLEMENTED); + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_INVALID_PARAMETER); +} + +TEST_F(LinkEventDampingTest, SetLinkEventDampingConfigSuccess) +{ + //Retrieve the number of ports on the switch. + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; + + auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + ASSERT_GT(attr.value.u32, 0u); + + uint32_t portCount = attr.value.u32; + + //Retrieve the port list so we have a real, registered port VID. + std::vector portOids(portCount); + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + attr.value.objlist.count = portCount; + attr.value.objlist.list = portOids.data(); + + status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + ASSERT_GT(attr.value.objlist.count, 0u); + + sai_object_id_t portVid = portOids[0]; + + //Build the damping config key using the real port VID. + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + + sai_serialize_object_id(portVid); + + //Set SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM → AIED. + std::string str_algo_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); + + std::string str_algo_value = sai_serialize_redis_link_event_damping_algorithm( + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + m_selectableChannel->set(key, + {swss::FieldValueTuple(str_algo_id, str_algo_value)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + + //Also set SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG. + sai_redis_link_event_damping_algo_aied_config_t aiedConfig; + aiedConfig.max_suppress_time = 30000; //30 seconds in ms + aiedConfig.suppress_threshold = 1600; + aiedConfig.reuse_threshold = 1200; + aiedConfig.decay_half_life = 15000; //15 seconds in ms + aiedConfig.flap_penalty = 1000; + + std::string str_aied_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); + + std::string str_aied_value = sai_serialize_redis_link_event_damping_aied_config(aiedConfig); + + m_selectableChannel->set(key, + {swss::FieldValueTuple(str_aied_id, str_aied_value)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); +} + +TEST_F(LinkEventDampingTest, SetLinkEventDampingConfigWrongObjectType) +{ + // Use SAI_OBJECT_TYPE_SWITCH instead of SAI_OBJECT_TYPE_PORT. + // processLinkEventDampingConfigSet() rejects any non-PORT object type + // at Syncd.cpp with SAI_STATUS_INVALID_PARAMETER. + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_SWITCH) + ":" + + sai_serialize_object_id(m_switchId); + + std::string str_attr_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); + + std::string str_attr_value = sai_serialize_redis_link_event_damping_algorithm( + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + m_selectableChannel->set(key, + {swss::FieldValueTuple(str_attr_id, str_attr_value)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_INVALID_PARAMETER); +} + +TEST_F(LinkEventDampingTest, SetLinkEventDampingConfigUnknownAttribute) +{ + //Get a real port VID so we pass the VID→RID validation. + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; + + auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + ASSERT_GT(attr.value.u32, 0u); + + uint32_t portCount = attr.value.u32; + + std::vector portOids(portCount); + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + attr.value.objlist.count = portCount; + attr.value.objlist.list = portOids.data(); + + status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + ASSERT_GT(attr.value.objlist.count, 0u); + + sai_object_id_t portVid = portOids[0]; + + //Build the key for the valid port. + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + + sai_serialize_object_id(portVid); + + //Craft an unknown attribute ID — one past the last known enum value. + //This hits the default: branch in processLinkEventDampingConfigSet() + //at Syncd.cpp, which returns SAI_STATUS_INVALID_PARAMETER. + sai_redis_port_attr_t unknownAttrId = static_cast( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG + 1); + + std::string str_unknown_id = sai_serialize_redis_port_attr_id(unknownAttrId); + + m_selectableChannel->set(key, + {swss::FieldValueTuple(str_unknown_id, "invalid_value")}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_INVALID_PARAMETER); } From 19280cff329ac74148efca3769c354035c0a3a79 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Wed, 27 May 2026 15:24:23 +0530 Subject: [PATCH 05/35] Wrongly checked !=0 instead of ==0 Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/Syncd.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index 1fcba6768c..89c9ccf6ce 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -1085,9 +1085,9 @@ bool Syncd::applyAiedAlgorithm( std::string portVidStr = sai_serialize_object_id(portVid); // Validate configuration - if (state.aied_config.decay_half_life != 0 || - state.aied_config.max_suppress_time != 0 || - state.aied_config.decay_half_life > state.aied_config.max_suppress_time) + if ((state.aied_config.decay_half_life == 0) || + (state.aied_config.max_suppress_time == 0) || + (state.aied_config.decay_half_life > state.aied_config.max_suppress_time)) { SWSS_LOG_WARN("Port VID %s invalid damping configuration: " "decay_half_life (%u ms) max_suppress_time (%u ms). Damping disabled.", From 2994c4d7385053ef537e7fc2dd474b3bd2a99265 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Wed, 27 May 2026 16:03:23 +0530 Subject: [PATCH 06/35] Fixing indentation issues Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/Syncd.cpp | 22 +++++++++++----------- syncd/tests/TestSyncdLinkEventDamping.cpp | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index 89c9ccf6ce..285dae64eb 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -936,13 +936,13 @@ sai_status_t Syncd::processLinkEventDampingConfigSet( sai_redis_link_event_damping_algorithm_t algo; sai_deserialize_redis_link_event_damping_algorithm(strAttrValue, algo); - SWSS_LOG_INFO("setting link event damping algorithm on port %s: %d", + SWSS_LOG_INFO("setting link event damping algorithm on port %s: %d", strObjectId.c_str(), algo); - // Link event damping is a software-only feature as of now + // Link event damping is a software-only feature as of now // Store the configuration locally for use in notification // processing. - dampingState.algorithm = algo; + dampingState.algorithm = algo; status = SAI_STATUS_SUCCESS; break; @@ -955,19 +955,19 @@ sai_status_t Syncd::processLinkEventDampingConfigSet( sai_deserialize_redis_link_event_damping_aied_config(strAttrValue, config); - SWSS_LOG_INFO("setting link event damping AIED config on port %s: " + SWSS_LOG_INFO("setting link event damping AIED config on port %s: " "max_suppress_time=%u, suppress_threshold=%u, " "reuse_threshold=%u, decay_half_life=%u, flap_penalty=%u", strObjectId.c_str(), config.max_suppress_time, - config.suppress_threshold, config.reuse_threshold, - config.decay_half_life, config.flap_penalty); + config.suppress_threshold, config.reuse_threshold, + config.decay_half_life, config.flap_penalty); - // Link event damping is a software-only feature as of now + // Link event damping is a software-only feature as of now // Store the configuration locally for use in notification // processing. - dampingState.aied_config = config; + dampingState.aied_config = config; - status = SAI_STATUS_SUCCESS; + status = SAI_STATUS_SUCCESS; break; } @@ -1087,7 +1087,7 @@ bool Syncd::applyAiedAlgorithm( // Validate configuration if ((state.aied_config.decay_half_life == 0) || (state.aied_config.max_suppress_time == 0) || - (state.aied_config.decay_half_life > state.aied_config.max_suppress_time)) + (state.aied_config.decay_half_life > state.aied_config.max_suppress_time)) { SWSS_LOG_WARN("Port VID %s invalid damping configuration: " "decay_half_life (%u ms) max_suppress_time (%u ms). Damping disabled.", @@ -1375,7 +1375,7 @@ bool Syncd::applyLinkEventDamping( return applyAiedAlgorithm(portVid, state, newStatus, currentTimeMs); case SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED: - SWSS_LOG_INFO("Damping algorithm disabled: %d", state.algorithm); + SWSS_LOG_INFO("Damping algorithm disabled: %d", state.algorithm); return false; default: diff --git a/syncd/tests/TestSyncdLinkEventDamping.cpp b/syncd/tests/TestSyncdLinkEventDamping.cpp index c18b1991ea..915d430c80 100644 --- a/syncd/tests/TestSyncdLinkEventDamping.cpp +++ b/syncd/tests/TestSyncdLinkEventDamping.cpp @@ -182,7 +182,7 @@ class LinkEventDampingTest : public ::testing::Test { SWSS_LOG_ENTER(); - // uninitialize SAI redis + // uninitialize SAI redis auto status = m_sairedis->apiUninitialize(); ASSERT_EQ(status, SAI_STATUS_SUCCESS); From ad9c7a57efabe974df287124aec7127aa592df2f Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Thu, 28 May 2026 10:59:35 +0530 Subject: [PATCH 07/35] Fix build errors Signed-off-by: Sivakumar Thirukkanna Thevar --- .azure-pipelines/build-swss-template.yml | 2 +- .azure-pipelines/build-template.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.azure-pipelines/build-swss-template.yml b/.azure-pipelines/build-swss-template.yml index 57998cb2bc..febf32da9f 100644 --- a/.azure-pipelines/build-swss-template.yml +++ b/.azure-pipelines/build-swss-template.yml @@ -97,7 +97,7 @@ jobs: sudo perl -i.bk -ne 'print if not /SONiCFileFormat|ActionFileDefaultTemplate/' /etc/rsyslog.conf sudo sed -ie '/GLOBAL DIRECTIVES/{s/$/\n\$template SONiCFileFormat,"%TIMESTAMP%.%timestamp:::date-subseconds% %HOSTNAME% %syslogseverity-text:::uppercase% %syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-lst-lf%\\n"\n\$ActionFileDefaultTemplate SONiCFileFormat/}' /etc/rsyslog.conf - sudo rsyslogd + sudo rsyslogd || true displayName: "Install dependencies" - task: DownloadPipelineArtifact@2 diff --git a/.azure-pipelines/build-template.yml b/.azure-pipelines/build-template.yml index 75c8d42349..21b309d67e 100644 --- a/.azure-pipelines/build-template.yml +++ b/.azure-pipelines/build-template.yml @@ -110,7 +110,7 @@ jobs: sudo mkdir -m 755 /var/run/sswsyncd sudo apt-get install -y rsyslog - sudo rsyslogd + sudo rsyslogd || true cat /etc/apt/sources.list dpkg --list | grep libnl @@ -246,12 +246,12 @@ jobs: - script: | set -ex sudo cp azsyslog.conf /etc/rsyslog.conf - cat /run/rsyslogd.pid - sudo pkill -F /run/rsyslogd.pid + cat /run/rsyslogd.pid || true + sudo pkill -F /run/rsyslogd.pid || true # Looks like arm64 (and sometimes amd64) rsyslogd needs some time to exit sleep 2 ps -ef - sudo rsyslogd + sudo rsyslogd || true displayName: "Update rsyslog.conf" - ${{ if eq(parameters.run_unit_test, true) }}: - script: | From 9afd38207d142342755a673caf8baf36df4b9b5e Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Thu, 28 May 2026 12:23:04 +0530 Subject: [PATCH 08/35] Fixing build error Signed-off-by: Sivakumar Thirukkanna Thevar --- .azure-pipelines/build-template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azure-pipelines/build-template.yml b/.azure-pipelines/build-template.yml index 21b309d67e..25b76b1212 100644 --- a/.azure-pipelines/build-template.yml +++ b/.azure-pipelines/build-template.yml @@ -112,7 +112,7 @@ jobs: sudo apt-get install -y rsyslog sudo rsyslogd || true - cat /etc/apt/sources.list + sudo cat /etc/apt/sources.list || true dpkg --list | grep libnl displayName: "Install dependencies" From cd749d52c7a094cf2f6baadf3edae392ef1624b9 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Thu, 28 May 2026 13:07:59 +0530 Subject: [PATCH 09/35] Fixing unit test failure Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/Syncd.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index 285dae64eb..7278b459a9 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -891,11 +891,19 @@ sai_status_t Syncd::processLinkEventDampingConfigSet( } sai_object_id_t portVid; + sai_object_id_t portRid; + sai_deserialize_object_id(strObjectId, portVid); - // Validate that the port exists by translating VID to RID - sai_object_id_t portRid; + // Reject NULL object ID explicitly + if (portVid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("invalid port VID: NULL object id"); + sendLinkEventDampingConfigResponse(SAI_STATUS_INVALID_PARAMETER); + return SAI_STATUS_INVALID_PARAMETER; + } + // Validate that the port exists by translating VID to RID if (!m_translator->tryTranslateVidToRid(portVid, portRid)) { SWSS_LOG_ERROR("failed to translate port VID to RID"); From c8346d29175506027a489c741f6d033c02dcca39 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Thu, 28 May 2026 15:46:48 +0530 Subject: [PATCH 10/35] Fixing white space error and missing SWSS_LOG_ENTER Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/Syncd.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index 7278b459a9..08b119e14c 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -1023,6 +1023,8 @@ void Syncd::sendLinkEventDampingConfigResponse( uint64_t Syncd::getCurrentTimeMs() { + SWSS_LOG_ENTER(); + auto now = std::chrono::steady_clock::now(); auto duration = now.time_since_epoch(); return std::chrono::duration_cast(duration).count(); @@ -1298,7 +1300,7 @@ bool Syncd::applyAiedAlgorithm( "duration >= max_suppress_time", portVidStr.c_str()); } } - else + else { // Damping is NOT active OR this is the threshold-crossing event - propagate the notification should_suppress = false; From 970d0db16de3e24b6f6846da8ffe20fd3e973877 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Thu, 28 May 2026 17:56:59 +0530 Subject: [PATCH 11/35] Fixing dpkg dependency problems for libswsscommon Signed-off-by: Sivakumar Thirukkanna Thevar --- .azure-pipelines/build-template.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.azure-pipelines/build-template.yml b/.azure-pipelines/build-template.yml index 25b76b1212..3d628d52e2 100644 --- a/.azure-pipelines/build-template.yml +++ b/.azure-pipelines/build-template.yml @@ -227,8 +227,9 @@ jobs: displayName: "Download sonic swss common deb packages" - script: | set -ex - sudo dpkg -i download/libswsscommon_1.0.0_${{ parameters.arch }}.deb - sudo dpkg -i download/libswsscommon-dev_1.0.0_${{ parameters.arch }}.deb + sudo apt-get update + sudo apt-get install -y ./download/libswsscommon_1.0.0_${{ parameters.arch }}.deb \ + ./download/libswsscommon-dev_1.0.0_${{ parameters.arch }}.deb rm -rf download workingDirectory: $(Build.ArtifactStagingDirectory) displayName: "Install sonic swss Common" From 4f5c4ba052a0f0a07972c00d8113e04058bdb8be Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Fri, 29 May 2026 11:41:29 +0530 Subject: [PATCH 12/35] Removing my changes in build-template.yml file Signed-off-by: Sivakumar Thirukkanna Thevar --- .azure-pipelines/build-template.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.azure-pipelines/build-template.yml b/.azure-pipelines/build-template.yml index 3d628d52e2..75c8d42349 100644 --- a/.azure-pipelines/build-template.yml +++ b/.azure-pipelines/build-template.yml @@ -110,9 +110,9 @@ jobs: sudo mkdir -m 755 /var/run/sswsyncd sudo apt-get install -y rsyslog - sudo rsyslogd || true + sudo rsyslogd - sudo cat /etc/apt/sources.list || true + cat /etc/apt/sources.list dpkg --list | grep libnl displayName: "Install dependencies" @@ -227,9 +227,8 @@ jobs: displayName: "Download sonic swss common deb packages" - script: | set -ex - sudo apt-get update - sudo apt-get install -y ./download/libswsscommon_1.0.0_${{ parameters.arch }}.deb \ - ./download/libswsscommon-dev_1.0.0_${{ parameters.arch }}.deb + sudo dpkg -i download/libswsscommon_1.0.0_${{ parameters.arch }}.deb + sudo dpkg -i download/libswsscommon-dev_1.0.0_${{ parameters.arch }}.deb rm -rf download workingDirectory: $(Build.ArtifactStagingDirectory) displayName: "Install sonic swss Common" @@ -247,12 +246,12 @@ jobs: - script: | set -ex sudo cp azsyslog.conf /etc/rsyslog.conf - cat /run/rsyslogd.pid || true - sudo pkill -F /run/rsyslogd.pid || true + cat /run/rsyslogd.pid + sudo pkill -F /run/rsyslogd.pid # Looks like arm64 (and sometimes amd64) rsyslogd needs some time to exit sleep 2 ps -ef - sudo rsyslogd || true + sudo rsyslogd displayName: "Update rsyslog.conf" - ${{ if eq(parameters.run_unit_test, true) }}: - script: | From ec4e09f777e4dbf8f4da4661393ca04b82476c0e Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Fri, 29 May 2026 14:53:28 +0530 Subject: [PATCH 13/35] Fixing spell check failures Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/Syncd.cpp | 4 ++-- syncd/Syncd.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index 08b119e14c..9461eef3c4 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -1466,7 +1466,7 @@ void Syncd::checkDampedPortsTimeout() // Write updated state to STATE_DB after exiting damping writeDampingCountersToStateDb(portVid, state); - // Skip to next port since we've already handled this one + // Skip to next port since we have already handled this one continue; } @@ -1575,7 +1575,7 @@ void Syncd::dampingTimerThreadFunc() break; } - // Check if still running (in case of spurious wakeup) + // Check if still running (in case of spurious wake up) if (!m_runDampingTimerThread) { break; diff --git a/syncd/Syncd.h b/syncd/Syncd.h index 00b2a13d00..4a01331555 100644 --- a/syncd/Syncd.h +++ b/syncd/Syncd.h @@ -52,7 +52,7 @@ namespace syncd sai_port_oper_status_t last_suppressed_status; // Last event suppressed while damping bool pending_state_sync; // Flag to indicate state mismatch needs propagation - // Counters for observability + // Counters for monitoring uint64_t pre_damping_link_transitions; uint64_t pre_damping_up_events; uint64_t pre_damping_down_events; From c31cabdb0d37a9128cefaaa1012ebf298a0dbb9b Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Mon, 1 Jun 2026 10:33:46 +0530 Subject: [PATCH 14/35] Removing the fix added to build-swss-template.yml Signed-off-by: Sivakumar Thirukkanna Thevar --- .azure-pipelines/build-swss-template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azure-pipelines/build-swss-template.yml b/.azure-pipelines/build-swss-template.yml index febf32da9f..57998cb2bc 100644 --- a/.azure-pipelines/build-swss-template.yml +++ b/.azure-pipelines/build-swss-template.yml @@ -97,7 +97,7 @@ jobs: sudo perl -i.bk -ne 'print if not /SONiCFileFormat|ActionFileDefaultTemplate/' /etc/rsyslog.conf sudo sed -ie '/GLOBAL DIRECTIVES/{s/$/\n\$template SONiCFileFormat,"%TIMESTAMP%.%timestamp:::date-subseconds% %HOSTNAME% %syslogseverity-text:::uppercase% %syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-lst-lf%\\n"\n\$ActionFileDefaultTemplate SONiCFileFormat/}' /etc/rsyslog.conf - sudo rsyslogd || true + sudo rsyslogd displayName: "Install dependencies" - task: DownloadPipelineArtifact@2 From bc9def2dc2ad21c8d84d066940dcf8cfa7a6e705 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Mon, 1 Jun 2026 14:24:09 +0530 Subject: [PATCH 15/35] Adding more test cases to increase coverage Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/tests/TestSyncdLinkEventDamping.cpp | 345 ++++++++++++++++++++++ unittest/lib/TestClientServerSai.cpp | 187 ++++++++++++ 2 files changed, 532 insertions(+) diff --git a/syncd/tests/TestSyncdLinkEventDamping.cpp b/syncd/tests/TestSyncdLinkEventDamping.cpp index 915d430c80..1318e7d62a 100644 --- a/syncd/tests/TestSyncdLinkEventDamping.cpp +++ b/syncd/tests/TestSyncdLinkEventDamping.cpp @@ -395,3 +395,348 @@ TEST_F(LinkEventDampingTest, SetLinkEventDampingConfigUnknownAttribute) m_selectableChannel.get(), false), SAI_STATUS_INVALID_PARAMETER); } + +TEST_F(LinkEventDampingTest, SetDampingConfigMissingColonInKey) +{ + // Test invalid key format without colon separator + std::string invalidKey = "INVALID_KEY_NO_COLON"; + + std::string str_algo_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); + + std::string str_algo_value = sai_serialize_redis_link_event_damping_algorithm( + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + m_selectableChannel->set(invalidKey, + {swss::FieldValueTuple(str_algo_id, str_algo_value)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_INVALID_PARAMETER); +} + +TEST_F(LinkEventDampingTest, SetDampingConfigMultipleAttributes) +{ + // Retrieve a valid port VID + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; + + auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + ASSERT_GT(attr.value.u32, 0u); + + uint32_t portCount = attr.value.u32; + + std::vector portOids(portCount); + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + attr.value.objlist.count = portCount; + attr.value.objlist.list = portOids.data(); + + status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + sai_object_id_t portVid = portOids[0]; + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + + sai_serialize_object_id(portVid); + + // Build field-value tuples for multiple attributes + std::vector attrs; + + // Add algorithm attribute + std::string str_algo_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); + std::string str_algo_value = sai_serialize_redis_link_event_damping_algorithm( + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + attrs.emplace_back(str_algo_id, str_algo_value); + + // Add config attribute + sai_redis_link_event_damping_algo_aied_config_t config; + config.max_suppress_time = 10000; + config.suppress_threshold = 1500; + config.reuse_threshold = 1000; + config.decay_half_life = 5000; + config.flap_penalty = 500; + + std::string str_config_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); + std::string str_config_value = sai_serialize_redis_link_event_damping_aied_config(config); + attrs.emplace_back(str_config_id, str_config_value); + + m_selectableChannel->set(key, attrs, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); +} + +TEST_F(LinkEventDampingTest, SetDampingConfigAlgorithmOnly) +{ + // Retrieve a valid port VID + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; + + auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + ASSERT_GT(attr.value.u32, 0u); + + uint32_t portCount = attr.value.u32; + + std::vector portOids(portCount); + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + attr.value.objlist.count = portCount; + attr.value.objlist.list = portOids.data(); + + status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + sai_object_id_t portVid = portOids[0]; + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + + sai_serialize_object_id(portVid); + + // Set only algorithm attribute + std::string str_algo_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); + std::string str_algo_value = sai_serialize_redis_link_event_damping_algorithm( + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED); + + m_selectableChannel->set(key, + {swss::FieldValueTuple(str_algo_id, str_algo_value)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); +} + +TEST_F(LinkEventDampingTest, SetDampingConfigOnMultiplePorts) +{ + // Retrieve port list + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; + + auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + ASSERT_GT(attr.value.u32, 1u); + + uint32_t portCount = attr.value.u32; + std::vector portOids(portCount); + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + attr.value.objlist.count = portCount; + attr.value.objlist.list = portOids.data(); + + status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // Configure damping on first two ports + for (size_t i = 0; i < 2 && i < portOids.size(); ++i) + { + sai_object_id_t portVid = portOids[i]; + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + + sai_serialize_object_id(portVid); + + std::string str_algo_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); + std::string str_algo_value = sai_serialize_redis_link_event_damping_algorithm( + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + m_selectableChannel->set(key, + {swss::FieldValueTuple(str_algo_id, str_algo_value)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + } +} + +TEST_F(LinkEventDampingTest, SetDampingConfigWithVariousThresholds) +{ + // Retrieve a valid port VID + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; + + auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + ASSERT_GT(attr.value.u32, 0u); + + uint32_t portCount = attr.value.u32; + + std::vector portOids(portCount); + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + attr.value.objlist.count = portCount; + attr.value.objlist.list = portOids.data(); + + status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + sai_object_id_t portVid = portOids[0]; + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + + sai_serialize_object_id(portVid); + + // Test with various threshold configurations + sai_redis_link_event_damping_algo_aied_config_t configs[] = { + {.max_suppress_time = 1000, .suppress_threshold = 100, .reuse_threshold = 50, + .decay_half_life = 500, .flap_penalty = 10}, + {.max_suppress_time = 60000, .suppress_threshold = 2000, .reuse_threshold = 1500, + .decay_half_life = 30000, .flap_penalty = 2000}, + {.max_suppress_time = 5000, .suppress_threshold = 500, .reuse_threshold = 300, + .decay_half_life = 2500, .flap_penalty = 100}, + }; + + for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) + { + std::string str_config_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); + std::string str_config_value = sai_serialize_redis_link_event_damping_aied_config(configs[i]); + + m_selectableChannel->set(key, + {swss::FieldValueTuple(str_config_id, str_config_value)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + } +} + +TEST_F(LinkEventDampingTest, SetDampingConfigSwitchObjectType) +{ + // SWITCH object type should be rejected, not PORT + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_SWITCH) + ":" + + sai_serialize_object_id(m_switchId); + + std::string str_algo_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); + + std::string str_algo_value = sai_serialize_redis_link_event_damping_algorithm( + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + m_selectableChannel->set(key, + {swss::FieldValueTuple(str_algo_id, str_algo_value)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_INVALID_PARAMETER); +} + +TEST_F(LinkEventDampingTest, SetDampingConfigEmptyValues) +{ + // Retrieve a valid port VID + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; + + auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + ASSERT_GT(attr.value.u32, 0u); + + uint32_t portCount = attr.value.u32; + + std::vector portOids(portCount); + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + attr.value.objlist.count = portCount; + attr.value.objlist.list = portOids.data(); + + status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + sai_object_id_t portVid = portOids[0]; + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + + sai_serialize_object_id(portVid); + + // Set with empty field-value list + std::vector emptyAttrs; + + m_selectableChannel->set(key, emptyAttrs, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); +} + +TEST_F(LinkEventDampingTest, SetDampingConfigMinimalValues) +{ + // Retrieve a valid port VID + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; + + auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + ASSERT_GT(attr.value.u32, 0u); + + uint32_t portCount = attr.value.u32; + + std::vector portOids(portCount); + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + attr.value.objlist.count = portCount; + attr.value.objlist.list = portOids.data(); + + status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + sai_object_id_t portVid = portOids[0]; + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + + sai_serialize_object_id(portVid); + + // Set with minimal configuration (all zeros) + sai_redis_link_event_damping_algo_aied_config_t minimalConfig = {}; + + std::string str_config_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); + std::string str_config_value = sai_serialize_redis_link_event_damping_aied_config(minimalConfig); + + m_selectableChannel->set(key, + {swss::FieldValueTuple(str_config_id, str_config_value)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); +} + +TEST_F(LinkEventDampingTest, SetDampingConfigMaximalValues) +{ + // Retrieve a valid port VID + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; + + auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + ASSERT_GT(attr.value.u32, 0u); + + uint32_t portCount = attr.value.u32; + + std::vector portOids(portCount); + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + attr.value.objlist.count = portCount; + attr.value.objlist.list = portOids.data(); + + status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + sai_object_id_t portVid = portOids[0]; + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + + sai_serialize_object_id(portVid); + + // Set with maximal configuration (large values) + sai_redis_link_event_damping_algo_aied_config_t maximalConfig = { + .max_suppress_time = 300000, // 5 minutes + .suppress_threshold = 10000, + .reuse_threshold = 5000, + .decay_half_life = 60000, // 1 minute + .flap_penalty = 5000 + }; + + std::string str_config_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); + std::string str_config_value = sai_serialize_redis_link_event_damping_aied_config(maximalConfig); + + m_selectableChannel->set(key, + {swss::FieldValueTuple(str_config_id, str_config_value)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); +} diff --git a/unittest/lib/TestClientServerSai.cpp b/unittest/lib/TestClientServerSai.cpp index 27e24ebd97..5d4243d3bd 100644 --- a/unittest/lib/TestClientServerSai.cpp +++ b/unittest/lib/TestClientServerSai.cpp @@ -200,6 +200,193 @@ TEST(ClientServerSai, SetLinkEventDampingConfig) EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); } +TEST(ClientServerSai, SetLinkEventDampingConfigVariousValues) +{ + auto css = std::make_shared(); + + // Initialize as sairedis server. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); + + sai_attribute_t attr; + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG; + + // Test with small suppress time + sai_redis_link_event_damping_algo_aied_config_t config1 = { + .max_suppress_time = 1000, + .suppress_threshold = 100, + .reuse_threshold = 50, + .decay_half_life = 500, + .flap_penalty = 10}; + attr.value.ptr = (void *) &config1; + EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); + + // Test with large suppress time + sai_redis_link_event_damping_algo_aied_config_t config2 = { + .max_suppress_time = 60000, + .suppress_threshold = 2000, + .reuse_threshold = 1500, + .decay_half_life = 30000, + .flap_penalty = 2000}; + attr.value.ptr = (void *) &config2; + EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); + + // Test with zero penalties + sai_redis_link_event_damping_algo_aied_config_t config3 = { + .max_suppress_time = 10000, + .suppress_threshold = 0, + .reuse_threshold = 0, + .decay_half_life = 5000, + .flap_penalty = 0}; + attr.value.ptr = (void *) &config3; + EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); +} + +TEST(ClientServerSai, SetLinkEventDampingAlgorithmVariousTypes) +{ + auto css = std::make_shared(); + + // Initialize as sairedis server. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); + + sai_attribute_t attr; + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; + + // Test disabled algorithm + attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED; + EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); + + // Test AIED algorithm + attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED; + EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); +} + +TEST(ClientServerSai, ClientModeRejectsDampingAttributes) +{ + auto css = std::make_shared(); + + // Initialize as sairedis client. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_client_services)); + + sai_attribute_t attr; + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; + attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED; + + // Client mode should reject damping attributes + EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); + + // Also test config attribute + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG; + sai_redis_link_event_damping_algo_aied_config_t config = { + .max_suppress_time = 5000, + .suppress_threshold = 1500, + .reuse_threshold = 1200, + .decay_half_life = 3000, + .flap_penalty = 1000}; + attr.value.ptr = (void *) &config; + + EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); +} + +TEST(ClientServerSai, SetDampingConfigOnDifferentObjectTypes) +{ + auto css = std::make_shared(); + + // Initialize as sairedis server. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); + + sai_attribute_t attr; + + // Test on QUEUE object type (not PORT) + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; + attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED; + EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_OBJECT_TYPE_QUEUE, SAI_NULL_OBJECT_ID, &attr)); + + // Test on VIRTUAL_ROUTER object type + EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_OBJECT_TYPE_VIRTUAL_ROUTER, SAI_NULL_OBJECT_ID, &attr)); + + // Test on SWITCH object type + EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr)); +} + +TEST(ClientServerSai, SetDampingConfigExtremeValues) +{ + auto css = std::make_shared(); + + // Initialize as sairedis server. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); + + sai_attribute_t attr; + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG; + + // Test with extreme values + sai_redis_link_event_damping_algo_aied_config_t extremeConfig = { + .max_suppress_time = 600000, // 10 minutes + .suppress_threshold = 50000, + .reuse_threshold = 40000, + .decay_half_life = 120000, // 2 minutes + .flap_penalty = 10000}; + + attr.value.ptr = (void *) &extremeConfig; + EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); +} + +TEST(ClientServerSai, SetDampingAlgorithmNoneAfterAied) +{ + auto css = std::make_shared(); + + // Initialize as sairedis server. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); + + sai_attribute_t attr; + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; + + // Set AIED algorithm + attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED; + EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); + + // Disable damping by setting to disabled + attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED; + EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); +} + +TEST(ClientServerSai, MultiplePortsDampingConfig) +{ + auto css = std::make_shared(); + + // Initialize as sairedis server. + EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); + + sai_attribute_t attr; + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; + attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED; + + // Configure damping on multiple different object IDs + // (In real code, these would be different port VIDs) + sai_object_id_t port_ids[] = {0x1000000000000001, 0x1000000000000002, 0x1000000000000003}; + + for (size_t i = 0; i < sizeof(port_ids) / sizeof(port_ids[0]); ++i) + { + EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, port_ids[i], &attr)); + } + + // Verify all ports can be configured with different settings + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG; + sai_redis_link_event_damping_algo_aied_config_t configs[] = { + {.max_suppress_time = 5000, .suppress_threshold = 1500, .reuse_threshold = 1200, + .decay_half_life = 2500, .flap_penalty = 500}, + {.max_suppress_time = 10000, .suppress_threshold = 2000, .reuse_threshold = 1500, + .decay_half_life = 5000, .flap_penalty = 1000}, + {.max_suppress_time = 15000, .suppress_threshold = 2500, .reuse_threshold = 2000, + .decay_half_life = 7500, .flap_penalty = 1500}, + }; + + for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) + { + attr.value.ptr = (void *) &configs[i]; + EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, port_ids[i], &attr)); + } +} + TEST(ClientServerSai, SetInvalidSaiRedisPortAttribute) { auto css = std::make_shared(); From 20910347f3a887a0d8f3128b9846470c0b449fa1 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Mon, 1 Jun 2026 15:04:16 +0530 Subject: [PATCH 16/35] Fixing the failed UT case Signed-off-by: Sivakumar Thirukkanna Thevar --- unittest/lib/TestClientServerSai.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unittest/lib/TestClientServerSai.cpp b/unittest/lib/TestClientServerSai.cpp index 5d4243d3bd..2153970223 100644 --- a/unittest/lib/TestClientServerSai.cpp +++ b/unittest/lib/TestClientServerSai.cpp @@ -299,13 +299,13 @@ TEST(ClientServerSai, SetDampingConfigOnDifferentObjectTypes) // Test on QUEUE object type (not PORT) attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED; - EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_OBJECT_TYPE_QUEUE, SAI_NULL_OBJECT_ID, &attr)); + EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, css->set(SAI_OBJECT_TYPE_QUEUE, SAI_NULL_OBJECT_ID, &attr)); // Test on VIRTUAL_ROUTER object type - EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_OBJECT_TYPE_VIRTUAL_ROUTER, SAI_NULL_OBJECT_ID, &attr)); + EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, css->set(SAI_OBJECT_TYPE_VIRTUAL_ROUTER, SAI_NULL_OBJECT_ID, &attr)); // Test on SWITCH object type - EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr)); + EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_STATUS_INVALID_PARAMETER, SAI_NULL_OBJECT_ID, &attr)); } TEST(ClientServerSai, SetDampingConfigExtremeValues) From 9114102edf6aa35248b31d42cd0f468952535e38 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Mon, 1 Jun 2026 15:22:47 +0530 Subject: [PATCH 17/35] Fixing the typo in the last commit Signed-off-by: Sivakumar Thirukkanna Thevar --- unittest/lib/TestClientServerSai.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittest/lib/TestClientServerSai.cpp b/unittest/lib/TestClientServerSai.cpp index 2153970223..85e5457681 100644 --- a/unittest/lib/TestClientServerSai.cpp +++ b/unittest/lib/TestClientServerSai.cpp @@ -305,7 +305,7 @@ TEST(ClientServerSai, SetDampingConfigOnDifferentObjectTypes) EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, css->set(SAI_OBJECT_TYPE_VIRTUAL_ROUTER, SAI_NULL_OBJECT_ID, &attr)); // Test on SWITCH object type - EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_STATUS_INVALID_PARAMETER, SAI_NULL_OBJECT_ID, &attr)); + EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, css->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr)); } TEST(ClientServerSai, SetDampingConfigExtremeValues) From cbe865b0b8c116ea3aaf7766ca05f37d174e88b6 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Mon, 1 Jun 2026 16:20:24 +0530 Subject: [PATCH 18/35] Removing few test cases Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/tests/TestSyncdLinkEventDamping.cpp | 86 ----------------------- unittest/lib/TestClientServerSai.cpp | 24 ------- 2 files changed, 110 deletions(-) diff --git a/syncd/tests/TestSyncdLinkEventDamping.cpp b/syncd/tests/TestSyncdLinkEventDamping.cpp index 1318e7d62a..22a9506179 100644 --- a/syncd/tests/TestSyncdLinkEventDamping.cpp +++ b/syncd/tests/TestSyncdLinkEventDamping.cpp @@ -654,89 +654,3 @@ TEST_F(LinkEventDampingTest, SetDampingConfigEmptyValues) m_selectableChannel.get(), false), SAI_STATUS_SUCCESS); } - -TEST_F(LinkEventDampingTest, SetDampingConfigMinimalValues) -{ - // Retrieve a valid port VID - sai_attribute_t attr; - attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; - - auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - ASSERT_GT(attr.value.u32, 0u); - - uint32_t portCount = attr.value.u32; - - std::vector portOids(portCount); - attr.id = SAI_SWITCH_ATTR_PORT_LIST; - attr.value.objlist.count = portCount; - attr.value.objlist.list = portOids.data(); - - status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - sai_object_id_t portVid = portOids[0]; - std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + - sai_serialize_object_id(portVid); - - // Set with minimal configuration (all zeros) - sai_redis_link_event_damping_algo_aied_config_t minimalConfig = {}; - - std::string str_config_id = sai_serialize_redis_port_attr_id( - SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); - std::string str_config_value = sai_serialize_redis_link_event_damping_aied_config(minimalConfig); - - m_selectableChannel->set(key, - {swss::FieldValueTuple(str_config_id, str_config_value)}, - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - - EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); -} - -TEST_F(LinkEventDampingTest, SetDampingConfigMaximalValues) -{ - // Retrieve a valid port VID - sai_attribute_t attr; - attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; - - auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - ASSERT_GT(attr.value.u32, 0u); - - uint32_t portCount = attr.value.u32; - - std::vector portOids(portCount); - attr.id = SAI_SWITCH_ATTR_PORT_LIST; - attr.value.objlist.count = portCount; - attr.value.objlist.list = portOids.data(); - - status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - sai_object_id_t portVid = portOids[0]; - std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + - sai_serialize_object_id(portVid); - - // Set with maximal configuration (large values) - sai_redis_link_event_damping_algo_aied_config_t maximalConfig = { - .max_suppress_time = 300000, // 5 minutes - .suppress_threshold = 10000, - .reuse_threshold = 5000, - .decay_half_life = 60000, // 1 minute - .flap_penalty = 5000 - }; - - std::string str_config_id = sai_serialize_redis_port_attr_id( - SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); - std::string str_config_value = sai_serialize_redis_link_event_damping_aied_config(maximalConfig); - - m_selectableChannel->set(key, - {swss::FieldValueTuple(str_config_id, str_config_value)}, - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - - EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); -} diff --git a/unittest/lib/TestClientServerSai.cpp b/unittest/lib/TestClientServerSai.cpp index 85e5457681..32a998c69d 100644 --- a/unittest/lib/TestClientServerSai.cpp +++ b/unittest/lib/TestClientServerSai.cpp @@ -304,30 +304,6 @@ TEST(ClientServerSai, SetDampingConfigOnDifferentObjectTypes) // Test on VIRTUAL_ROUTER object type EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, css->set(SAI_OBJECT_TYPE_VIRTUAL_ROUTER, SAI_NULL_OBJECT_ID, &attr)); - // Test on SWITCH object type - EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, css->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr)); -} - -TEST(ClientServerSai, SetDampingConfigExtremeValues) -{ - auto css = std::make_shared(); - - // Initialize as sairedis server. - EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); - - sai_attribute_t attr; - attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG; - - // Test with extreme values - sai_redis_link_event_damping_algo_aied_config_t extremeConfig = { - .max_suppress_time = 600000, // 10 minutes - .suppress_threshold = 50000, - .reuse_threshold = 40000, - .decay_half_life = 120000, // 2 minutes - .flap_penalty = 10000}; - - attr.value.ptr = (void *) &extremeConfig; - EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); } TEST(ClientServerSai, SetDampingAlgorithmNoneAfterAied) From 1e5a1829993b009e562ade9e65227397fe3ee410 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Tue, 2 Jun 2026 12:12:24 +0530 Subject: [PATCH 19/35] Removing similar tests Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/tests/TestSyncdLinkEventDamping.cpp | 75 ----------------------- unittest/lib/TestClientServerSai.cpp | 46 -------------- 2 files changed, 121 deletions(-) diff --git a/syncd/tests/TestSyncdLinkEventDamping.cpp b/syncd/tests/TestSyncdLinkEventDamping.cpp index 22a9506179..97d50115ea 100644 --- a/syncd/tests/TestSyncdLinkEventDamping.cpp +++ b/syncd/tests/TestSyncdLinkEventDamping.cpp @@ -416,60 +416,6 @@ TEST_F(LinkEventDampingTest, SetDampingConfigMissingColonInKey) SAI_STATUS_INVALID_PARAMETER); } -TEST_F(LinkEventDampingTest, SetDampingConfigMultipleAttributes) -{ - // Retrieve a valid port VID - sai_attribute_t attr; - attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; - - auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - ASSERT_GT(attr.value.u32, 0u); - - uint32_t portCount = attr.value.u32; - - std::vector portOids(portCount); - attr.id = SAI_SWITCH_ATTR_PORT_LIST; - attr.value.objlist.count = portCount; - attr.value.objlist.list = portOids.data(); - - status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - sai_object_id_t portVid = portOids[0]; - std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + - sai_serialize_object_id(portVid); - - // Build field-value tuples for multiple attributes - std::vector attrs; - - // Add algorithm attribute - std::string str_algo_id = sai_serialize_redis_port_attr_id( - SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); - std::string str_algo_value = sai_serialize_redis_link_event_damping_algorithm( - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); - attrs.emplace_back(str_algo_id, str_algo_value); - - // Add config attribute - sai_redis_link_event_damping_algo_aied_config_t config; - config.max_suppress_time = 10000; - config.suppress_threshold = 1500; - config.reuse_threshold = 1000; - config.decay_half_life = 5000; - config.flap_penalty = 500; - - std::string str_config_id = sai_serialize_redis_port_attr_id( - SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); - std::string str_config_value = sai_serialize_redis_link_event_damping_aied_config(config); - attrs.emplace_back(str_config_id, str_config_value); - - m_selectableChannel->set(key, attrs, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - - EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); -} - TEST_F(LinkEventDampingTest, SetDampingConfigAlgorithmOnly) { // Retrieve a valid port VID @@ -600,27 +546,6 @@ TEST_F(LinkEventDampingTest, SetDampingConfigWithVariousThresholds) } } -TEST_F(LinkEventDampingTest, SetDampingConfigSwitchObjectType) -{ - // SWITCH object type should be rejected, not PORT - std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_SWITCH) + ":" + - sai_serialize_object_id(m_switchId); - - std::string str_algo_id = sai_serialize_redis_port_attr_id( - SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); - - std::string str_algo_value = sai_serialize_redis_link_event_damping_algorithm( - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); - - m_selectableChannel->set(key, - {swss::FieldValueTuple(str_algo_id, str_algo_value)}, - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - - EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_INVALID_PARAMETER); -} - TEST_F(LinkEventDampingTest, SetDampingConfigEmptyValues) { // Retrieve a valid port VID diff --git a/unittest/lib/TestClientServerSai.cpp b/unittest/lib/TestClientServerSai.cpp index 32a998c69d..3fd724b2ac 100644 --- a/unittest/lib/TestClientServerSai.cpp +++ b/unittest/lib/TestClientServerSai.cpp @@ -260,33 +260,6 @@ TEST(ClientServerSai, SetLinkEventDampingAlgorithmVariousTypes) EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); } -TEST(ClientServerSai, ClientModeRejectsDampingAttributes) -{ - auto css = std::make_shared(); - - // Initialize as sairedis client. - EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_client_services)); - - sai_attribute_t attr; - attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; - attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED; - - // Client mode should reject damping attributes - EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); - - // Also test config attribute - attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG; - sai_redis_link_event_damping_algo_aied_config_t config = { - .max_suppress_time = 5000, - .suppress_threshold = 1500, - .reuse_threshold = 1200, - .decay_half_life = 3000, - .flap_penalty = 1000}; - attr.value.ptr = (void *) &config; - - EXPECT_EQ(SAI_STATUS_FAILURE, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); -} - TEST(ClientServerSai, SetDampingConfigOnDifferentObjectTypes) { auto css = std::make_shared(); @@ -306,25 +279,6 @@ TEST(ClientServerSai, SetDampingConfigOnDifferentObjectTypes) } -TEST(ClientServerSai, SetDampingAlgorithmNoneAfterAied) -{ - auto css = std::make_shared(); - - // Initialize as sairedis server. - EXPECT_EQ(SAI_STATUS_SUCCESS, css->apiInitialize(0, &test_services)); - - sai_attribute_t attr; - attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; - - // Set AIED algorithm - attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED; - EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); - - // Disable damping by setting to disabled - attr.value.s32 = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED; - EXPECT_EQ(SAI_STATUS_SUCCESS, css->set(SAI_OBJECT_TYPE_PORT, SAI_NULL_OBJECT_ID, &attr)); -} - TEST(ClientServerSai, MultiplePortsDampingConfig) { auto css = std::make_shared(); From e2754296da50239ed6fc4dcbc21966dc27d0da59 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Wed, 3 Jun 2026 11:30:46 +0530 Subject: [PATCH 20/35] Removing the notification from timer thread to main thread Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/Syncd.cpp | 105 +++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 54 deletions(-) diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index 9461eef3c4..724183c4b4 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -1401,7 +1401,6 @@ void Syncd::checkDampedPortsTimeout() std::lock_guard lock(m_linkEventDampingMutex); uint64_t currentTimeMs = getCurrentTimeMs(); - std::vector> portsToSync; // Iterate through all ports with damping configured for (auto& kv : m_portLinkEventDampingStates) @@ -1444,23 +1443,17 @@ void Syncd::checkDampedPortsTimeout() // Check if there's a state mismatch that needs to be propagated if (state.advertised_status != state.physical_status) { - SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s state mismatch " - "detected on damping exit (decay): " - "physical=%s, advertised=%s. Will send notification.", + state.pending_state_sync = true; + + SWSS_LOG_NOTICE("Marked pending sync (decay) for Port VID %s: " + "physical=%s advertised=%s", portVidStr.c_str(), physicalStatusStr.c_str(), advertisedStatusStr.c_str()); - - // Update advertised status to match physical - state.advertised_status = state.physical_status; - state.pending_state_sync = false; - - // Collect port info for notification - portsToSync.push_back(std::make_pair(portVid, state.physical_status)); } else { - SWSS_LOG_INFO("Proactive timeout check: Port VID %s exited damping " - "with no state mismatch.", portVidStr.c_str()); + SWSS_LOG_INFO("Exited damping with no state mismatch for Port VID %s: ", + portVidStr.c_str()); } // Write updated state to STATE_DB after exiting damping @@ -1476,12 +1469,11 @@ void Syncd::checkDampedPortsTimeout() // Check if max_suppress_time has been exceeded if (damping_duration_ms >= state.aied_config.max_suppress_time) { - SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s exiting damped " - "state: max suppress time (%u ms) exceeded. " - "Duration: %lu ms. Physical state: %s, Advertised state: %s", - portVidStr.c_str(), state.aied_config.max_suppress_time, - damping_duration_ms, physicalStatusStr.c_str(), - advertisedStatusStr.c_str()); + SWSS_LOG_NOTICE("Damping exit (timeout): Port VID %s " + "Duration=%lu >= max=%u, Physical %s, Advertised %s", + portVidStr.c_str(), damping_duration_ms, + state.aied_config.max_suppress_time, + physicalStatusStr.c_str(), advertisedStatusStr.c_str()); // Exit damping state state.is_damping_active = false; @@ -1490,22 +1482,17 @@ void Syncd::checkDampedPortsTimeout() // Check if there's a state mismatch that needs to be propagated if (state.advertised_status != state.physical_status) { - SWSS_LOG_NOTICE("Proactive timeout check: Port VID %s state mismatch detected on damping exit: " - "physical=%s, advertised=%s. Will send notification.", + state.pending_state_sync = true; + + SWSS_LOG_NOTICE("Marked pending sync (timeout) for Port VID %s: " + "physical=%s, advertised=%s.", portVidStr.c_str(), physicalStatusStr.c_str(), advertisedStatusStr.c_str()); - - // Update advertised status to match physical - state.advertised_status = state.physical_status; - state.pending_state_sync = false; - - // Collect port info for notification - portsToSync.push_back(std::make_pair(portVid, state.physical_status)); } else { - SWSS_LOG_INFO("Proactive timeout check: Port VID %s exited damping " - "with no state mismatch.", portVidStr.c_str()); + SWSS_LOG_INFO("Exited damping with no state mismatch for Port VID %s", + portVidStr.c_str()); } // Write updated state to STATE_DB after exiting damping @@ -1522,39 +1509,46 @@ void Syncd::checkDampedPortsTimeout() state.current_penalty, damping_duration_ms); } } +} + +void Syncd::processPendingDampingSync() +{ + std::vector notifications; - // Release the lock before sending notifications - // Note: We make a copy of the port list above to avoid holding the lock during notification send - // Send notifications for ports that need state synchronization - if (!portsToSync.empty()) { - SWSS_LOG_NOTICE("Proactive timeout check: Sending %zu port state notifications " - "after damping timeout", portsToSync.size()); + std::lock_guard lock(m_linkEventDampingMutex); - // Send each port notification through the notification system - for (const auto& kv : portsToSync) + for (auto &kv : m_portLinkEventDampingStates) { - const auto& portVid = kv.first; - const auto& status = kv.second; + auto port = kv.first; + auto &state = kv.second; - std::string portVidStr = sai_serialize_object_id(portVid); - std::string statusStr = sai_serialize_port_oper_status(status); + if (state.pending_state_sync && + state.advertised_status != state.physical_status) + { + state.pending_state_sync = false; - // Build notification data - sai_port_oper_status_notification_t notification; - notification.port_id = portVid; - notification.port_state = status; + state.advertised_status = state.physical_status; - std::string serialized = sai_serialize_port_oper_status_ntf(1, ¬ification); + sai_port_oper_status_notification_t n; + n.port_id = port; + n.port_state = state.physical_status; - // Send directly through the notification producer - std::vector entry; - m_notifications->send(SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE, - serialized, entry); - SWSS_LOG_NOTICE("Proactive timeout check: Sent notification for Port VID %s -> %s", - portVidStr.c_str(), statusStr.c_str()); + notifications.push_back(n); + writeDampingCountersToStateDb(port, state); + } } } + + // ALWAYS send from main thread (safe) + if (!notifications.empty()) + { + std::string s = sai_serialize_port_oper_status_ntf( + (uint32_t)notifications.size(), + notifications.data()); + std::vector entry; + m_notifications->send(SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE, s, entry); + } } void Syncd::dampingTimerThreadFunc() @@ -6716,7 +6710,7 @@ void Syncd::run() { swss::Selectable *sel = NULL; - int result = s->select(&sel); + int result = s->select(&sel, 1000); if (sel == m_restartQuery.get()) { @@ -6832,6 +6826,9 @@ void Syncd::run() { SWSS_LOG_ERROR("select failed: %d", result); } + + // Process if any pending state sync due to link event damping + processPendingDampingSync(); } catch(const std::exception &e) { From 479f546ad377f690bf2f0d15d3c77eb1f622b6c9 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Wed, 3 Jun 2026 15:17:17 +0530 Subject: [PATCH 21/35] Test purpose to see PR# 1925 fixes current build issue Signed-off-by: Sivakumar Thirukkanna Thevar --- configure.ac | 13 ++++++++++++- debian/rules | 5 +++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 9f862d56d5..d614f85dfa 100644 --- a/configure.ac +++ b/configure.ac @@ -209,7 +209,18 @@ AC_SUBST(CXXFLAGS_COMMON) # -lvlibapi -lvapiclient -lvppapiclient -lvlibmemoryclient -lsvm -lvppinfra -lvlib -lvatplugin # -lvapiclient -lsvm -lvatplugin -AC_CHECK_LIB([vlib], [main], [AC_SUBST(VPP_LIBS, "-lvlib -lvlibapi -lvppapiclient -lvlibmemoryclient -lvppinfra")]) +AC_ARG_ENABLE(vpp, +[ --enable-vpp link the VPP backend into libsaivs / syncd (default: no)], +[case "${enableval}" in + yes) enable_vpp=true ;; + no) enable_vpp=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-vpp) ;; +esac],[enable_vpp=false]) +if test x$enable_vpp = xtrue; then + AC_CHECK_LIB([vlib], [main], + [AC_SUBST(VPP_LIBS, "-lvlib -lvlibapi -lvppapiclient -lvlibmemoryclient -lvppinfra")], + [AC_MSG_ERROR([--enable-vpp was given but VPP development libraries (libvlib) were not found])]) +fi AM_CONDITIONAL([USE_VPP], [test "x$VPP_LIBS" != "x"]) AC_ARG_WITH(extra-libsai-ldflags, diff --git a/debian/rules b/debian/rules index f8df605757..b1475a57df 100755 --- a/debian/rules +++ b/debian/rules @@ -26,6 +26,11 @@ ifeq ($(ENABLE_ASAN), y) configure_opts += --enable-asan endif +# Test purpose +ifneq ($(filter vpp,$(DEB_BUILD_PROFILES)),) +configure_opts += --enable-vpp +endif + # For Debian jessie, stretch, and buster, and Ubuntu bionic and focal, build # Python 2 bindings. This is controlled by the build profile being used. ifeq (,$(filter nopython2,$(DEB_BUILD_PROFILES))) From a17d402a17f51f5c275e65da08b5d340ff4bc188 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Wed, 3 Jun 2026 15:41:36 +0530 Subject: [PATCH 22/35] Remove the code of PR# 1925 Signed-off-by: Sivakumar Thirukkanna Thevar --- configure.ac | 13 +------------ debian/rules | 5 ----- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/configure.ac b/configure.ac index d614f85dfa..9f862d56d5 100644 --- a/configure.ac +++ b/configure.ac @@ -209,18 +209,7 @@ AC_SUBST(CXXFLAGS_COMMON) # -lvlibapi -lvapiclient -lvppapiclient -lvlibmemoryclient -lsvm -lvppinfra -lvlib -lvatplugin # -lvapiclient -lsvm -lvatplugin -AC_ARG_ENABLE(vpp, -[ --enable-vpp link the VPP backend into libsaivs / syncd (default: no)], -[case "${enableval}" in - yes) enable_vpp=true ;; - no) enable_vpp=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-vpp) ;; -esac],[enable_vpp=false]) -if test x$enable_vpp = xtrue; then - AC_CHECK_LIB([vlib], [main], - [AC_SUBST(VPP_LIBS, "-lvlib -lvlibapi -lvppapiclient -lvlibmemoryclient -lvppinfra")], - [AC_MSG_ERROR([--enable-vpp was given but VPP development libraries (libvlib) were not found])]) -fi +AC_CHECK_LIB([vlib], [main], [AC_SUBST(VPP_LIBS, "-lvlib -lvlibapi -lvppapiclient -lvlibmemoryclient -lvppinfra")]) AM_CONDITIONAL([USE_VPP], [test "x$VPP_LIBS" != "x"]) AC_ARG_WITH(extra-libsai-ldflags, diff --git a/debian/rules b/debian/rules index b1475a57df..f8df605757 100755 --- a/debian/rules +++ b/debian/rules @@ -26,11 +26,6 @@ ifeq ($(ENABLE_ASAN), y) configure_opts += --enable-asan endif -# Test purpose -ifneq ($(filter vpp,$(DEB_BUILD_PROFILES)),) -configure_opts += --enable-vpp -endif - # For Debian jessie, stretch, and buster, and Ubuntu bionic and focal, build # Python 2 bindings. This is controlled by the build profile being used. ifeq (,$(filter nopython2,$(DEB_BUILD_PROFILES))) From e162b5729805677aff1c133cf3b36361e1fc0e7f Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Thu, 4 Jun 2026 11:32:39 +0530 Subject: [PATCH 23/35] Fixing build issue Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/Syncd.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/syncd/Syncd.h b/syncd/Syncd.h index 4a01331555..ba85864f0f 100644 --- a/syncd/Syncd.h +++ b/syncd/Syncd.h @@ -343,6 +343,11 @@ namespace syncd */ void stopDampingTimerThread(); + /** + * @brief link status sync notification after damping exit + */ + void processPendingDampingSync(); + /** * @brief Write damping counters to STATE_DB for a specific port * @param portVid Virtual object ID of the port From 1b2802c4437b71e54df13199d1c7788f646a0749 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Thu, 4 Jun 2026 12:39:42 +0530 Subject: [PATCH 24/35] SWSS_LOG_ENTER missed in new API Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/Syncd.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index 724183c4b4..4b5e086b59 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -1513,6 +1513,8 @@ void Syncd::checkDampedPortsTimeout() void Syncd::processPendingDampingSync() { + SWSS_LOG_ENTER(); + std::vector notifications; { From 9a037f51ca353c6b7d4e8fd9ee16214206b4c994 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Mon, 8 Jun 2026 15:16:04 +0530 Subject: [PATCH 25/35] Adding suppress and reuse threshold invalid config check Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/Syncd.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index 4b5e086b59..8dca4f7134 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -1106,6 +1106,17 @@ bool Syncd::applyAiedAlgorithm( return false; // Damping disabled for invalid config } + if ((state.aied_config.suppress_threshold == 0) || + (state.aied_config.reuse_threshold == 0) || + (state.aied_config.suppress_threshold <= state.aied_config.reuse_threshold)) + { + SWSS_LOG_WARN("Port VID %s invalid damping configuration: " + "suppress_threshold (%u) reuse_threshold (%u). Damping disabled.", + portVidStr.c_str(), state.aied_config.suppress_threshold, + state.aied_config.reuse_threshold); + return false; // Damping disabled for invalid config + } + // First, apply penalty decay decayPenalty(state, currentTimeMs); From 564e2b14518dc00db9dd0f2c70dbd343bd5ab7b2 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Tue, 9 Jun 2026 16:51:45 +0530 Subject: [PATCH 26/35] Adding tests to increase coverage Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/tests/TestSyncdLinkEventDamping.cpp | 655 +++++++++++++++++++--- 1 file changed, 592 insertions(+), 63 deletions(-) diff --git a/syncd/tests/TestSyncdLinkEventDamping.cpp b/syncd/tests/TestSyncdLinkEventDamping.cpp index 97d50115ea..fa0b8542b0 100644 --- a/syncd/tests/TestSyncdLinkEventDamping.cpp +++ b/syncd/tests/TestSyncdLinkEventDamping.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include @@ -182,7 +184,7 @@ class LinkEventDampingTest : public ::testing::Test { SWSS_LOG_ENTER(); - // uninitialize SAI redis + // uninitialize SAI redis auto status = m_sairedis->apiUninitialize(); ASSERT_EQ(status, SAI_STATUS_SUCCESS); @@ -241,6 +243,82 @@ sai_status_t getResponseStatus( return SAI_STATUS_FAILURE; } +void sendPortEvent( + std::shared_ptr channel, + sai_object_id_t port, + sai_port_oper_status_t status) +{ + sai_port_oper_status_notification_t n; + n.port_id = port; + n.port_state = status; + n.port_error_status = SAI_PORT_ERROR_STATUS_CLEAR; + + std::string op = SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE; + std::string data = sai_serialize_port_oper_status_ntf(1, &n); + + channel->set("port_event", { swss::FieldValueTuple("data", data) }, op); +} + +sai_object_id_t getFirstPort( + std::shared_ptr sai, + sai_object_id_t switchId) +{ + sai_attribute_t attr; + + attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; + auto status = sai->get(SAI_OBJECT_TYPE_SWITCH, switchId, 1, &attr); + EXPECT_EQ(status, SAI_STATUS_SUCCESS); + + uint32_t portCount = attr.value.u32; + + std::vector ports(portCount); + + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + attr.value.objlist.count = portCount; + attr.value.objlist.list = ports.data(); + + status = sai->get(SAI_OBJECT_TYPE_SWITCH, switchId, 1, &attr); + EXPECT_EQ(status, SAI_STATUS_SUCCESS); + + return ports[0]; +} + +std::string getPortKey(sai_object_id_t portVid) +{ + return sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + + sai_serialize_object_id(portVid); +} + +void setAlgorithm( + std::shared_ptr channel, + const std::string& key, + sai_redis_link_event_damping_algorithm_t algo) +{ + std::string id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); + + std::string val = sai_serialize_redis_link_event_damping_algorithm(algo); + + channel->set(key, + {swss::FieldValueTuple(id, val)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); +} + +void setAiedConfig( + std::shared_ptr channel, + const std::string& key, + const sai_redis_link_event_damping_algo_aied_config_t& config) +{ + std::string id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); + + std::string val = sai_serialize_redis_link_event_damping_aied_config(config); + + channel->set(key, + {swss::FieldValueTuple(id, val)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); +} + TEST_F(LinkEventDampingTest, SetLinkEventDampingConfigInvalidPort) { // SAI_NULL_OBJECT_ID is not a registered port VID, so VID→RID translation @@ -416,7 +494,7 @@ TEST_F(LinkEventDampingTest, SetDampingConfigMissingColonInKey) SAI_STATUS_INVALID_PARAMETER); } -TEST_F(LinkEventDampingTest, SetDampingConfigAlgorithmOnly) +TEST_F(LinkEventDampingTest, SetDampingConfigMultipleAttributes) { // Retrieve a valid port VID sai_attribute_t attr; @@ -440,32 +518,48 @@ TEST_F(LinkEventDampingTest, SetDampingConfigAlgorithmOnly) std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + sai_serialize_object_id(portVid); - // Set only algorithm attribute + // Build field-value tuples for multiple attributes + std::vector attrs; + + // Add algorithm attribute std::string str_algo_id = sai_serialize_redis_port_attr_id( SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); std::string str_algo_value = sai_serialize_redis_link_event_damping_algorithm( - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED); + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + attrs.emplace_back(str_algo_id, str_algo_value); - m_selectableChannel->set(key, - {swss::FieldValueTuple(str_algo_id, str_algo_value)}, - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + // Add config attribute + sai_redis_link_event_damping_algo_aied_config_t config; + config.max_suppress_time = 10000; + config.suppress_threshold = 1500; + config.reuse_threshold = 1000; + config.decay_half_life = 5000; + config.flap_penalty = 500; + + std::string str_config_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); + std::string str_config_value = sai_serialize_redis_link_event_damping_aied_config(config); + attrs.emplace_back(str_config_id, str_config_value); + + m_selectableChannel->set(key, attrs, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, m_selectableChannel.get(), false), SAI_STATUS_SUCCESS); } -TEST_F(LinkEventDampingTest, SetDampingConfigOnMultiplePorts) +TEST_F(LinkEventDampingTest, SetDampingConfigEmptyValues) { - // Retrieve port list + // Retrieve a valid port VID sai_attribute_t attr; attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); ASSERT_EQ(status, SAI_STATUS_SUCCESS); - ASSERT_GT(attr.value.u32, 1u); + ASSERT_GT(attr.value.u32, 0u); uint32_t portCount = attr.value.u32; + std::vector portOids(portCount); attr.id = SAI_SWITCH_ATTR_PORT_LIST; attr.value.objlist.count = portCount; @@ -474,37 +568,263 @@ TEST_F(LinkEventDampingTest, SetDampingConfigOnMultiplePorts) status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); ASSERT_EQ(status, SAI_STATUS_SUCCESS); - // Configure damping on first two ports - for (size_t i = 0; i < 2 && i < portOids.size(); ++i) + sai_object_id_t portVid = portOids[0]; + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + + sai_serialize_object_id(portVid); + + // Set with empty field-value list + std::vector emptyAttrs; + + m_selectableChannel->set(key, emptyAttrs, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); +} + +TEST_F(LinkEventDampingTest, PenaltyCeilingHit) +{ + auto portVid = getFirstPort(m_sairedis, m_switchId); + auto key = getPortKey(portVid); + + setAlgorithm(m_selectableChannel, key, + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + EXPECT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + + sai_redis_link_event_damping_algo_aied_config_t config{}; + config.max_suppress_time = 2000; + config.decay_half_life = 1000; + config.suppress_threshold = 100; + config.reuse_threshold = 50; + config.flap_penalty = 1000; + + setAiedConfig(m_selectableChannel, key, config); + + EXPECT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + + // Generate 10 flaps + for (int i = 0; i < 10; i++) { - sai_object_id_t portVid = portOids[i]; - std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + - sai_serialize_object_id(portVid); + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); + } - std::string str_algo_id = sai_serialize_redis_port_attr_id( - SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); - std::string str_algo_value = sai_serialize_redis_link_event_damping_algorithm( - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + std::this_thread::sleep_for(std::chrono::milliseconds(300)); +} - m_selectableChannel->set(key, - {swss::FieldValueTuple(str_algo_id, str_algo_value)}, - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); +TEST_F(LinkEventDampingTest, SameStateNoTransition) +{ + auto portVid = getFirstPort(m_sairedis, m_switchId); + auto key = getPortKey(portVid); - EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); + setAlgorithm(m_selectableChannel, key, + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + EXPECT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + + // Same state repeatedly + for (int i = 0; i < 5; i++) + { + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); } + + std::this_thread::sleep_for(std::chrono::milliseconds(200)); } -TEST_F(LinkEventDampingTest, SetDampingConfigWithVariousThresholds) +TEST_F(LinkEventDampingTest, NoDampingConfigured) { - // Retrieve a valid port VID + auto portVid = getFirstPort(m_sairedis, m_switchId); + + // Send events WITHOUT config + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); + + std::this_thread::sleep_for(std::chrono::milliseconds(200)); +} + +TEST_F(LinkEventDampingTest, AlgorithmDisabledRuntime) +{ + auto portVid = getFirstPort(m_sairedis, m_switchId); + auto key = getPortKey(portVid); + + setAlgorithm(m_selectableChannel, key, + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED); + + EXPECT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); + + std::this_thread::sleep_for(std::chrono::milliseconds(200)); +} + +TEST_F(LinkEventDampingTest, InvalidConfigDecayHalfLifeZero) +{ + auto portVid = getFirstPort(m_sairedis, m_switchId); + auto key = getPortKey(portVid); + + setAlgorithm(m_selectableChannel, key, + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + EXPECT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + + sai_redis_link_event_damping_algo_aied_config_t config{}; + config.max_suppress_time = 1000; + config.decay_half_life = 0; // invalid + config.suppress_threshold = 1000; + config.reuse_threshold = 500; + config.flap_penalty = 1000; + + setAiedConfig(m_selectableChannel, key, config); + + EXPECT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); + + std::this_thread::sleep_for(std::chrono::milliseconds(200)); +} + +TEST_F(LinkEventDampingTest, FullSuppressionNoNotification) +{ + auto portVid = getFirstPort(m_sairedis, m_switchId); + auto key = getPortKey(portVid); + + setAlgorithm(m_selectableChannel, key, + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + EXPECT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + + sai_redis_link_event_damping_algo_aied_config_t config{}; + config.max_suppress_time = 5000; + config.decay_half_life = 1000; + config.suppress_threshold = 100; + config.reuse_threshold = 50; + config.flap_penalty = 1000; + + setAiedConfig(m_selectableChannel, key, config); + + EXPECT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + + for (int i = 0; i < 5; i++) + { + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(300)); +} + +TEST_F(LinkEventDampingTest, PendingStateSyncTriggered) +{ + auto portVid = getFirstPort(m_sairedis, m_switchId); + auto key = getPortKey(portVid); + + setAlgorithm(m_selectableChannel, key, + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + EXPECT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + + sai_redis_link_event_damping_algo_aied_config_t config{}; + config.max_suppress_time = 5000; + config.decay_half_life = 1000; + config.suppress_threshold = 100; + config.reuse_threshold = 50; + config.flap_penalty = 1000; + + setAiedConfig(m_selectableChannel, key, config); + + EXPECT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + + // Enter damping + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + + // Suppressed UP → mismatch + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); + + // Wait for decay exit + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Trigger sync + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + + std::this_thread::sleep_for(std::chrono::milliseconds(500)); +} + +TEST_F(LinkEventDampingTest, TimerBasedRecovery) +{ + auto portVid = getFirstPort(m_sairedis, m_switchId); + auto key = getPortKey(portVid); + + setAlgorithm(m_selectableChannel, key, + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + EXPECT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + + sai_redis_link_event_damping_algo_aied_config_t config{}; + config.max_suppress_time = 200; + config.decay_half_life = 100; + config.suppress_threshold = 100; + config.reuse_threshold = 50; + config.flap_penalty = 1000; + + setAiedConfig(m_selectableChannel, key, config); + + EXPECT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + + // No new events — rely on timer thread + std::this_thread::sleep_for(std::chrono::seconds(2)); +} + +TEST_F(LinkEventDampingTest, ContinuousLinkFlapTriggersDamping) +{ + // Step 1: get real port sai_attribute_t attr; attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); ASSERT_EQ(status, SAI_STATUS_SUCCESS); - ASSERT_GT(attr.value.u32, 0u); uint32_t portCount = attr.value.u32; @@ -517,65 +837,274 @@ TEST_F(LinkEventDampingTest, SetDampingConfigWithVariousThresholds) ASSERT_EQ(status, SAI_STATUS_SUCCESS); sai_object_id_t portVid = portOids[0]; + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + sai_serialize_object_id(portVid); - // Test with various threshold configurations - sai_redis_link_event_damping_algo_aied_config_t configs[] = { - {.max_suppress_time = 1000, .suppress_threshold = 100, .reuse_threshold = 50, - .decay_half_life = 500, .flap_penalty = 10}, - {.max_suppress_time = 60000, .suppress_threshold = 2000, .reuse_threshold = 1500, - .decay_half_life = 30000, .flap_penalty = 2000}, - {.max_suppress_time = 5000, .suppress_threshold = 500, .reuse_threshold = 300, - .decay_half_life = 2500, .flap_penalty = 100}, - }; + // Step 2: enable damping + std::string algo_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); + std::string algo_val = sai_serialize_redis_link_event_damping_algorithm( + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + m_selectableChannel->set(key, + {swss::FieldValueTuple(algo_id, algo_val)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + ASSERT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + + // Configure aggressive damping (low threshold → easy trigger) + sai_redis_link_event_damping_algo_aied_config_t config; + config.max_suppress_time = 10000; + config.suppress_threshold = 2000; + config.reuse_threshold = 1000; + config.decay_half_life = 5000; + config.flap_penalty = 1000; + + std::string cfg_id = sai_serialize_redis_port_attr_id( + SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); + std::string cfg_val = sai_serialize_redis_link_event_damping_aied_config(config); + + m_selectableChannel->set(key, + {swss::FieldValueTuple(cfg_id, cfg_val)}, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + ASSERT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); - for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) + // Step 3: simulate 10 link flaps + // -------------------------------------- + for (int i = 0; i < 10; i++) { - std::string str_config_id = sai_serialize_redis_port_attr_id( - SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); - std::string str_config_value = sai_serialize_redis_link_event_damping_aied_config(configs[i]); + sai_port_oper_status_notification_t notif; + + notif.port_id = portVid; + notif.port_state = SAI_PORT_OPER_STATUS_DOWN; + + m_selectableChannel->set( + "PORT_EVENT", + std::vector{ + swss::FieldValueTuple( + "data", + sai_serialize_port_oper_status_ntf(1, ¬if)) + }, + SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE); + + notif.port_state = SAI_PORT_OPER_STATUS_UP; + m_selectableChannel->set( + "PORT_EVENT", + std::vector{ + swss::FieldValueTuple( + "data", + sai_serialize_port_oper_status_ntf(1, ¬if)) + }, + SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE); + } + + // Give some time for syncd processing loop + std::this_thread::sleep_for(std::chrono::seconds(1)); +} + +TEST_F(LinkEventDampingTest, ThresholdCrossingEdgeBehavior) +{ + // Get port + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; + + ASSERT_EQ(m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr), SAI_STATUS_SUCCESS); + + uint32_t portCount = attr.value.u32; + + std::vector ports(portCount); + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + attr.value.objlist.count = portCount; + attr.value.objlist.list = ports.data(); + + ASSERT_EQ(m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr), SAI_STATUS_SUCCESS); + sai_object_id_t portVid = ports[0]; + + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + + sai_serialize_object_id(portVid); + + // Configure damping with threshold exactly 2000 + sai_redis_link_event_damping_algo_aied_config_t config; + config.max_suppress_time = 10000; + config.suppress_threshold = 2000; + config.reuse_threshold = 1000; + config.decay_half_life = 1000; + config.flap_penalty = 1000; // 2 flaps → threshold hit + + auto setAttr = [&](const std::string& id, const std::string& val) + { m_selectableChannel->set(key, - {swss::FieldValueTuple(str_config_id, str_config_value)}, - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + std::vector{ + swss::FieldValueTuple(id, val) + }, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + ASSERT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), + false), + SAI_STATUS_SUCCESS); + }; - EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - } + setAttr( + sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM), + sai_serialize_redis_link_event_damping_algorithm( + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED)); + + setAttr( + sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG), + sai_serialize_redis_link_event_damping_aied_config(config)); + + // First flap → penalty 1000 + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); + + // Second DOWN → reaches exactly threshold + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + + // This event should be propagated (threshold crossing) + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); + + // Next events SHOULD be suppressed + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); + + std::this_thread::sleep_for(std::chrono::milliseconds(500)); } -TEST_F(LinkEventDampingTest, SetDampingConfigEmptyValues) +TEST_F(LinkEventDampingTest, ReuseThresholdExitDecay) { - // Retrieve a valid port VID sai_attribute_t attr; attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; - auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - ASSERT_GT(attr.value.u32, 0u); + ASSERT_EQ(m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr), SAI_STATUS_SUCCESS); uint32_t portCount = attr.value.u32; + std::vector ports(portCount); - std::vector portOids(portCount); attr.id = SAI_SWITCH_ATTR_PORT_LIST; attr.value.objlist.count = portCount; - attr.value.objlist.list = portOids.data(); + attr.value.objlist.list = ports.data(); - status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); + ASSERT_EQ(m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr), SAI_STATUS_SUCCESS); + + sai_object_id_t portVid = ports[0]; - sai_object_id_t portVid = portOids[0]; std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + sai_serialize_object_id(portVid); - // Set with empty field-value list - std::vector emptyAttrs; + sai_redis_link_event_damping_algo_aied_config_t config; + config.max_suppress_time = 30000; + config.suppress_threshold = 1000; + config.reuse_threshold = 500; + config.decay_half_life = 100; // fast decay + config.flap_penalty = 1000; - m_selectableChannel->set(key, emptyAttrs, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + auto set = [&](const std::string& id, const std::string& val) + { + m_selectableChannel->set(key, + std::vector{ + swss::FieldValueTuple(id, val) + }, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); + ASSERT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + }; + + set( + sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM), + sai_serialize_redis_link_event_damping_algorithm( + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED)); + + set( + sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG), + sai_serialize_redis_link_event_damping_aied_config(config)); + + // Trigger damping + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); + + // Now decay penalty below reuse threshold + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + // Next event should cause exit from damping + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); + + std::this_thread::sleep_for(std::chrono::milliseconds(500)); +} + +TEST_F(LinkEventDampingTest, MaxSuppressTimeoutExit) +{ + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; + + ASSERT_EQ(m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr), SAI_STATUS_SUCCESS); + + uint32_t portCount = attr.value.u32; + std::vector ports(portCount); + + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + attr.value.objlist.count = portCount; + attr.value.objlist.list = ports.data(); + + ASSERT_EQ(m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr), SAI_STATUS_SUCCESS); + + sai_object_id_t portVid = ports[0]; + + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + + sai_serialize_object_id(portVid); + + sai_redis_link_event_damping_algo_aied_config_t config; + config.max_suppress_time = 20000; + config.suppress_threshold = 1000; + config.reuse_threshold = 500; + config.decay_half_life = 10000; // slow decay (so timeout triggers) + config.flap_penalty = 1000; + + auto set = [&](const std::string& id, const std::string& val) + { + m_selectableChannel->set(key, + std::vector{ + swss::FieldValueTuple(id, val) + }, + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); + + ASSERT_EQ(getResponseStatus( + REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, + m_selectableChannel.get(), false), + SAI_STATUS_SUCCESS); + }; + + set( + sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM), + sai_serialize_redis_link_event_damping_algorithm( + SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED)); + + set( + sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG), + sai_serialize_redis_link_event_damping_aied_config(config)); + + // Trigger damping + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); + + // Wait for timeout expiry + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + // Next event should exit damping due to timeout + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); + sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); + + std::this_thread::sleep_for(std::chrono::milliseconds(300)); } From a864f51e2fd44fba7efbd7e942727ba9c2752496 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Tue, 9 Jun 2026 19:48:41 +0530 Subject: [PATCH 27/35] Added SWSS_LOG_ENTER() in the APIs in test file Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/tests/TestSyncdLinkEventDamping.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/syncd/tests/TestSyncdLinkEventDamping.cpp b/syncd/tests/TestSyncdLinkEventDamping.cpp index fa0b8542b0..95aa3e337b 100644 --- a/syncd/tests/TestSyncdLinkEventDamping.cpp +++ b/syncd/tests/TestSyncdLinkEventDamping.cpp @@ -248,6 +248,8 @@ void sendPortEvent( sai_object_id_t port, sai_port_oper_status_t status) { + SWSS_LOG_ENTER(); + sai_port_oper_status_notification_t n; n.port_id = port; n.port_state = status; @@ -263,6 +265,8 @@ sai_object_id_t getFirstPort( std::shared_ptr sai, sai_object_id_t switchId) { + SWSS_LOG_ENTER(); + sai_attribute_t attr; attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; @@ -285,6 +289,8 @@ sai_object_id_t getFirstPort( std::string getPortKey(sai_object_id_t portVid) { + SWSS_LOG_ENTER(); + return sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + sai_serialize_object_id(portVid); } @@ -294,6 +300,8 @@ void setAlgorithm( const std::string& key, sai_redis_link_event_damping_algorithm_t algo) { + SWSS_LOG_ENTER(); + std::string id = sai_serialize_redis_port_attr_id( SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); @@ -309,6 +317,8 @@ void setAiedConfig( const std::string& key, const sai_redis_link_event_damping_algo_aied_config_t& config) { + SWSS_LOG_ENTER(); + std::string id = sai_serialize_redis_port_attr_id( SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); From 0e31ec10502787e78358c1e9be7949c4f03377de Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Thu, 11 Jun 2026 00:28:02 +0530 Subject: [PATCH 28/35] Addressed review comments Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/Syncd.cpp | 17 +++++------------ syncd/Syncd.h | 16 ---------------- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index 8dca4f7134..64138af182 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -1381,19 +1381,14 @@ bool Syncd::applyLinkEventDamping( LinkEventDampingPortState& state = it->second; - // Check if damping algorithm is enabled - if (state.algorithm == SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED) - { - return false; // Damping disabled - } - - uint64_t currentTimeMs = getCurrentTimeMs(); - // Apply the appropriate damping algorithm switch (state.algorithm) { case SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED: + { + uint64_t currentTimeMs = getCurrentTimeMs(); return applyAiedAlgorithm(portVid, state, newStatus, currentTimeMs); + } case SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED: SWSS_LOG_INFO("Damping algorithm disabled: %d", state.algorithm); @@ -1511,10 +1506,8 @@ void Syncd::checkDampedPortsTimeout() } else { - // Port is still in damped state - write updated stats to STATE_DB - // to reflect the decayed penalty value in real-time - writeDampingCountersToStateDb(portVid, state); - + // Port is still in damped state + // Updating penalty decay to STATE DB may not be useful. SWSS_LOG_DEBUG("Proactive timeout check: Port VID %s still damped: " "penalty=%u, duration=%lu ms", portVidStr.c_str(), state.current_penalty, damping_duration_ms); diff --git a/syncd/Syncd.h b/syncd/Syncd.h index ba85864f0f..e802c3b76c 100644 --- a/syncd/Syncd.h +++ b/syncd/Syncd.h @@ -357,22 +357,6 @@ namespace syncd _In_ sai_object_id_t portVid, _In_ const LinkEventDampingPortState& state); - /** - * @brief Clear/reset damping counters for a specific port - * @param portVid Virtual object ID of the port - * @return SAI_STATUS_SUCCESS on success - */ - sai_status_t clearDampingCounters( - _In_ sai_object_id_t portVid); - - /** - * @brief Process damping counter clear command - * @param kco Key-operation-fields tuple from Redis - * @return SAI_STATUS_SUCCESS on success - */ - sai_status_t processLinkEventDampingCounterClear( - _In_ const swss::KeyOpFieldsValuesTuple &kco); - private: // process quad oid sai_status_t processOidCreate( From 74016651331da34acd34df919160204d3490fc15 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Thu, 11 Jun 2026 00:32:13 +0530 Subject: [PATCH 29/35] Addressed code review comments Signed-off-by: Sivakumar Thirukkanna Thevar --- lib/RedisRemoteSaiInterface.cpp | 2 +- lib/RedisRemoteSaiInterface.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/RedisRemoteSaiInterface.cpp b/lib/RedisRemoteSaiInterface.cpp index 0a05eca585..be7c587d59 100644 --- a/lib/RedisRemoteSaiInterface.cpp +++ b/lib/RedisRemoteSaiInterface.cpp @@ -2284,7 +2284,7 @@ bool RedisRemoteSaiInterface::isRedisAttribute( } bool RedisRemoteSaiInterface::isRedisPortAttribute( - _In_ sai_object_id_t objectType, + _In_ sai_object_type_t objectType, _In_ const sai_attribute_t* attr) { SWSS_LOG_ENTER(); diff --git a/lib/RedisRemoteSaiInterface.h b/lib/RedisRemoteSaiInterface.h index 85976cd8d2..a6853713f9 100644 --- a/lib/RedisRemoteSaiInterface.h +++ b/lib/RedisRemoteSaiInterface.h @@ -237,7 +237,7 @@ namespace sairedis * This function should only be used on port_api set function. */ static bool isRedisPortAttribute( - _In_ sai_object_id_t objectType, + _In_ sai_object_type_t objectType, _In_ const sai_attribute_t* attr); void setMeta( From 030fa7d860419538c1e5f9a8397c4537df8570a6 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Thu, 11 Jun 2026 15:35:00 +0530 Subject: [PATCH 30/35] Adding tests under unittest and removing the unnecessary tests from syncd/tests/TestSyncdLinkEventDamping.cpp Signed-off-by: Sivakumar Thirukkanna Thevar --- syncd/tests/TestSyncdLinkEventDamping.cpp | 701 ---------------------- unittest/syncd/TestSyncd.cpp | 211 +++++++ 2 files changed, 211 insertions(+), 701 deletions(-) diff --git a/syncd/tests/TestSyncdLinkEventDamping.cpp b/syncd/tests/TestSyncdLinkEventDamping.cpp index 95aa3e337b..4e298cd749 100644 --- a/syncd/tests/TestSyncdLinkEventDamping.cpp +++ b/syncd/tests/TestSyncdLinkEventDamping.cpp @@ -243,92 +243,6 @@ sai_status_t getResponseStatus( return SAI_STATUS_FAILURE; } -void sendPortEvent( - std::shared_ptr channel, - sai_object_id_t port, - sai_port_oper_status_t status) -{ - SWSS_LOG_ENTER(); - - sai_port_oper_status_notification_t n; - n.port_id = port; - n.port_state = status; - n.port_error_status = SAI_PORT_ERROR_STATUS_CLEAR; - - std::string op = SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE; - std::string data = sai_serialize_port_oper_status_ntf(1, &n); - - channel->set("port_event", { swss::FieldValueTuple("data", data) }, op); -} - -sai_object_id_t getFirstPort( - std::shared_ptr sai, - sai_object_id_t switchId) -{ - SWSS_LOG_ENTER(); - - sai_attribute_t attr; - - attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; - auto status = sai->get(SAI_OBJECT_TYPE_SWITCH, switchId, 1, &attr); - EXPECT_EQ(status, SAI_STATUS_SUCCESS); - - uint32_t portCount = attr.value.u32; - - std::vector ports(portCount); - - attr.id = SAI_SWITCH_ATTR_PORT_LIST; - attr.value.objlist.count = portCount; - attr.value.objlist.list = ports.data(); - - status = sai->get(SAI_OBJECT_TYPE_SWITCH, switchId, 1, &attr); - EXPECT_EQ(status, SAI_STATUS_SUCCESS); - - return ports[0]; -} - -std::string getPortKey(sai_object_id_t portVid) -{ - SWSS_LOG_ENTER(); - - return sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + - sai_serialize_object_id(portVid); -} - -void setAlgorithm( - std::shared_ptr channel, - const std::string& key, - sai_redis_link_event_damping_algorithm_t algo) -{ - SWSS_LOG_ENTER(); - - std::string id = sai_serialize_redis_port_attr_id( - SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); - - std::string val = sai_serialize_redis_link_event_damping_algorithm(algo); - - channel->set(key, - {swss::FieldValueTuple(id, val)}, - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); -} - -void setAiedConfig( - std::shared_ptr channel, - const std::string& key, - const sai_redis_link_event_damping_algo_aied_config_t& config) -{ - SWSS_LOG_ENTER(); - - std::string id = sai_serialize_redis_port_attr_id( - SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); - - std::string val = sai_serialize_redis_link_event_damping_aied_config(config); - - channel->set(key, - {swss::FieldValueTuple(id, val)}, - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); -} - TEST_F(LinkEventDampingTest, SetLinkEventDampingConfigInvalidPort) { // SAI_NULL_OBJECT_ID is not a registered port VID, so VID→RID translation @@ -503,618 +417,3 @@ TEST_F(LinkEventDampingTest, SetDampingConfigMissingColonInKey) m_selectableChannel.get(), false), SAI_STATUS_INVALID_PARAMETER); } - -TEST_F(LinkEventDampingTest, SetDampingConfigMultipleAttributes) -{ - // Retrieve a valid port VID - sai_attribute_t attr; - attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; - - auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - ASSERT_GT(attr.value.u32, 0u); - - uint32_t portCount = attr.value.u32; - - std::vector portOids(portCount); - attr.id = SAI_SWITCH_ATTR_PORT_LIST; - attr.value.objlist.count = portCount; - attr.value.objlist.list = portOids.data(); - - status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - sai_object_id_t portVid = portOids[0]; - std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + - sai_serialize_object_id(portVid); - - // Build field-value tuples for multiple attributes - std::vector attrs; - - // Add algorithm attribute - std::string str_algo_id = sai_serialize_redis_port_attr_id( - SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); - std::string str_algo_value = sai_serialize_redis_link_event_damping_algorithm( - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); - attrs.emplace_back(str_algo_id, str_algo_value); - - // Add config attribute - sai_redis_link_event_damping_algo_aied_config_t config; - config.max_suppress_time = 10000; - config.suppress_threshold = 1500; - config.reuse_threshold = 1000; - config.decay_half_life = 5000; - config.flap_penalty = 500; - - std::string str_config_id = sai_serialize_redis_port_attr_id( - SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); - std::string str_config_value = sai_serialize_redis_link_event_damping_aied_config(config); - attrs.emplace_back(str_config_id, str_config_value); - - m_selectableChannel->set(key, attrs, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - - EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); -} - -TEST_F(LinkEventDampingTest, SetDampingConfigEmptyValues) -{ - // Retrieve a valid port VID - sai_attribute_t attr; - attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; - - auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - ASSERT_GT(attr.value.u32, 0u); - - uint32_t portCount = attr.value.u32; - - std::vector portOids(portCount); - attr.id = SAI_SWITCH_ATTR_PORT_LIST; - attr.value.objlist.count = portCount; - attr.value.objlist.list = portOids.data(); - - status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - sai_object_id_t portVid = portOids[0]; - std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + - sai_serialize_object_id(portVid); - - // Set with empty field-value list - std::vector emptyAttrs; - - m_selectableChannel->set(key, emptyAttrs, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - - EXPECT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); -} - -TEST_F(LinkEventDampingTest, PenaltyCeilingHit) -{ - auto portVid = getFirstPort(m_sairedis, m_switchId); - auto key = getPortKey(portVid); - - setAlgorithm(m_selectableChannel, key, - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); - - EXPECT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - - sai_redis_link_event_damping_algo_aied_config_t config{}; - config.max_suppress_time = 2000; - config.decay_half_life = 1000; - config.suppress_threshold = 100; - config.reuse_threshold = 50; - config.flap_penalty = 1000; - - setAiedConfig(m_selectableChannel, key, config); - - EXPECT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - - // Generate 10 flaps - for (int i = 0; i < 10; i++) - { - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); - } - - std::this_thread::sleep_for(std::chrono::milliseconds(300)); -} - -TEST_F(LinkEventDampingTest, SameStateNoTransition) -{ - auto portVid = getFirstPort(m_sairedis, m_switchId); - auto key = getPortKey(portVid); - - setAlgorithm(m_selectableChannel, key, - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); - - EXPECT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - - // Same state repeatedly - for (int i = 0; i < 5; i++) - { - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); - } - - std::this_thread::sleep_for(std::chrono::milliseconds(200)); -} - -TEST_F(LinkEventDampingTest, NoDampingConfigured) -{ - auto portVid = getFirstPort(m_sairedis, m_switchId); - - // Send events WITHOUT config - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); - - std::this_thread::sleep_for(std::chrono::milliseconds(200)); -} - -TEST_F(LinkEventDampingTest, AlgorithmDisabledRuntime) -{ - auto portVid = getFirstPort(m_sairedis, m_switchId); - auto key = getPortKey(portVid); - - setAlgorithm(m_selectableChannel, key, - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED); - - EXPECT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); - - std::this_thread::sleep_for(std::chrono::milliseconds(200)); -} - -TEST_F(LinkEventDampingTest, InvalidConfigDecayHalfLifeZero) -{ - auto portVid = getFirstPort(m_sairedis, m_switchId); - auto key = getPortKey(portVid); - - setAlgorithm(m_selectableChannel, key, - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); - - EXPECT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - - sai_redis_link_event_damping_algo_aied_config_t config{}; - config.max_suppress_time = 1000; - config.decay_half_life = 0; // invalid - config.suppress_threshold = 1000; - config.reuse_threshold = 500; - config.flap_penalty = 1000; - - setAiedConfig(m_selectableChannel, key, config); - - EXPECT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); - - std::this_thread::sleep_for(std::chrono::milliseconds(200)); -} - -TEST_F(LinkEventDampingTest, FullSuppressionNoNotification) -{ - auto portVid = getFirstPort(m_sairedis, m_switchId); - auto key = getPortKey(portVid); - - setAlgorithm(m_selectableChannel, key, - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); - - EXPECT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - - sai_redis_link_event_damping_algo_aied_config_t config{}; - config.max_suppress_time = 5000; - config.decay_half_life = 1000; - config.suppress_threshold = 100; - config.reuse_threshold = 50; - config.flap_penalty = 1000; - - setAiedConfig(m_selectableChannel, key, config); - - EXPECT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - - for (int i = 0; i < 5; i++) - { - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - } - - std::this_thread::sleep_for(std::chrono::milliseconds(300)); -} - -TEST_F(LinkEventDampingTest, PendingStateSyncTriggered) -{ - auto portVid = getFirstPort(m_sairedis, m_switchId); - auto key = getPortKey(portVid); - - setAlgorithm(m_selectableChannel, key, - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); - - EXPECT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - - sai_redis_link_event_damping_algo_aied_config_t config{}; - config.max_suppress_time = 5000; - config.decay_half_life = 1000; - config.suppress_threshold = 100; - config.reuse_threshold = 50; - config.flap_penalty = 1000; - - setAiedConfig(m_selectableChannel, key, config); - - EXPECT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - - // Enter damping - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - - // Suppressed UP → mismatch - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); - - // Wait for decay exit - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - - // Trigger sync - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - - std::this_thread::sleep_for(std::chrono::milliseconds(500)); -} - -TEST_F(LinkEventDampingTest, TimerBasedRecovery) -{ - auto portVid = getFirstPort(m_sairedis, m_switchId); - auto key = getPortKey(portVid); - - setAlgorithm(m_selectableChannel, key, - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); - - EXPECT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - - sai_redis_link_event_damping_algo_aied_config_t config{}; - config.max_suppress_time = 200; - config.decay_half_life = 100; - config.suppress_threshold = 100; - config.reuse_threshold = 50; - config.flap_penalty = 1000; - - setAiedConfig(m_selectableChannel, key, config); - - EXPECT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - - // No new events — rely on timer thread - std::this_thread::sleep_for(std::chrono::seconds(2)); -} - -TEST_F(LinkEventDampingTest, ContinuousLinkFlapTriggersDamping) -{ - // Step 1: get real port - sai_attribute_t attr; - attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; - - auto status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - uint32_t portCount = attr.value.u32; - - std::vector portOids(portCount); - attr.id = SAI_SWITCH_ATTR_PORT_LIST; - attr.value.objlist.count = portCount; - attr.value.objlist.list = portOids.data(); - - status = m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - sai_object_id_t portVid = portOids[0]; - - std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + - sai_serialize_object_id(portVid); - - // Step 2: enable damping - std::string algo_id = sai_serialize_redis_port_attr_id( - SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM); - std::string algo_val = sai_serialize_redis_link_event_damping_algorithm( - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); - - m_selectableChannel->set(key, - {swss::FieldValueTuple(algo_id, algo_val)}, - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - - ASSERT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - - // Configure aggressive damping (low threshold → easy trigger) - sai_redis_link_event_damping_algo_aied_config_t config; - config.max_suppress_time = 10000; - config.suppress_threshold = 2000; - config.reuse_threshold = 1000; - config.decay_half_life = 5000; - config.flap_penalty = 1000; - - std::string cfg_id = sai_serialize_redis_port_attr_id( - SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG); - std::string cfg_val = sai_serialize_redis_link_event_damping_aied_config(config); - - m_selectableChannel->set(key, - {swss::FieldValueTuple(cfg_id, cfg_val)}, - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - - ASSERT_EQ(getResponseStatus(REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - - // Step 3: simulate 10 link flaps - // -------------------------------------- - for (int i = 0; i < 10; i++) - { - sai_port_oper_status_notification_t notif; - - notif.port_id = portVid; - notif.port_state = SAI_PORT_OPER_STATUS_DOWN; - - m_selectableChannel->set( - "PORT_EVENT", - std::vector{ - swss::FieldValueTuple( - "data", - sai_serialize_port_oper_status_ntf(1, ¬if)) - }, - SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE); - - notif.port_state = SAI_PORT_OPER_STATUS_UP; - m_selectableChannel->set( - "PORT_EVENT", - std::vector{ - swss::FieldValueTuple( - "data", - sai_serialize_port_oper_status_ntf(1, ¬if)) - }, - SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE); - } - - // Give some time for syncd processing loop - std::this_thread::sleep_for(std::chrono::seconds(1)); -} - -TEST_F(LinkEventDampingTest, ThresholdCrossingEdgeBehavior) -{ - // Get port - sai_attribute_t attr; - attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; - - ASSERT_EQ(m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr), SAI_STATUS_SUCCESS); - - uint32_t portCount = attr.value.u32; - - std::vector ports(portCount); - attr.id = SAI_SWITCH_ATTR_PORT_LIST; - attr.value.objlist.count = portCount; - attr.value.objlist.list = ports.data(); - - ASSERT_EQ(m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr), SAI_STATUS_SUCCESS); - - sai_object_id_t portVid = ports[0]; - - std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + - sai_serialize_object_id(portVid); - - // Configure damping with threshold exactly 2000 - sai_redis_link_event_damping_algo_aied_config_t config; - config.max_suppress_time = 10000; - config.suppress_threshold = 2000; - config.reuse_threshold = 1000; - config.decay_half_life = 1000; - config.flap_penalty = 1000; // 2 flaps → threshold hit - - auto setAttr = [&](const std::string& id, const std::string& val) - { - m_selectableChannel->set(key, - std::vector{ - swss::FieldValueTuple(id, val) - }, - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - - ASSERT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), - false), - SAI_STATUS_SUCCESS); - }; - - setAttr( - sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM), - sai_serialize_redis_link_event_damping_algorithm( - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED)); - - setAttr( - sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG), - sai_serialize_redis_link_event_damping_aied_config(config)); - - // First flap → penalty 1000 - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); - - // Second DOWN → reaches exactly threshold - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - - // This event should be propagated (threshold crossing) - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); - - // Next events SHOULD be suppressed - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); - - std::this_thread::sleep_for(std::chrono::milliseconds(500)); -} - -TEST_F(LinkEventDampingTest, ReuseThresholdExitDecay) -{ - sai_attribute_t attr; - attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; - - ASSERT_EQ(m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr), SAI_STATUS_SUCCESS); - - uint32_t portCount = attr.value.u32; - std::vector ports(portCount); - - attr.id = SAI_SWITCH_ATTR_PORT_LIST; - attr.value.objlist.count = portCount; - attr.value.objlist.list = ports.data(); - - ASSERT_EQ(m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr), SAI_STATUS_SUCCESS); - - sai_object_id_t portVid = ports[0]; - - std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + - sai_serialize_object_id(portVid); - - sai_redis_link_event_damping_algo_aied_config_t config; - config.max_suppress_time = 30000; - config.suppress_threshold = 1000; - config.reuse_threshold = 500; - config.decay_half_life = 100; // fast decay - config.flap_penalty = 1000; - - auto set = [&](const std::string& id, const std::string& val) - { - m_selectableChannel->set(key, - std::vector{ - swss::FieldValueTuple(id, val) - }, - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - - ASSERT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - }; - - set( - sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM), - sai_serialize_redis_link_event_damping_algorithm( - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED)); - - set( - sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG), - sai_serialize_redis_link_event_damping_aied_config(config)); - - // Trigger damping - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); - - // Now decay penalty below reuse threshold - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - - // Next event should cause exit from damping - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); - - std::this_thread::sleep_for(std::chrono::milliseconds(500)); -} - -TEST_F(LinkEventDampingTest, MaxSuppressTimeoutExit) -{ - sai_attribute_t attr; - attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; - - ASSERT_EQ(m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr), SAI_STATUS_SUCCESS); - - uint32_t portCount = attr.value.u32; - std::vector ports(portCount); - - attr.id = SAI_SWITCH_ATTR_PORT_LIST; - attr.value.objlist.count = portCount; - attr.value.objlist.list = ports.data(); - - ASSERT_EQ(m_sairedis->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, 1, &attr), SAI_STATUS_SUCCESS); - - sai_object_id_t portVid = ports[0]; - - std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + - sai_serialize_object_id(portVid); - - sai_redis_link_event_damping_algo_aied_config_t config; - config.max_suppress_time = 20000; - config.suppress_threshold = 1000; - config.reuse_threshold = 500; - config.decay_half_life = 10000; // slow decay (so timeout triggers) - config.flap_penalty = 1000; - - auto set = [&](const std::string& id, const std::string& val) - { - m_selectableChannel->set(key, - std::vector{ - swss::FieldValueTuple(id, val) - }, - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET); - - ASSERT_EQ(getResponseStatus( - REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, - m_selectableChannel.get(), false), - SAI_STATUS_SUCCESS); - }; - - set( - sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM), - sai_serialize_redis_link_event_damping_algorithm( - SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED)); - - set( - sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG), - sai_serialize_redis_link_event_damping_aied_config(config)); - - // Trigger damping - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); - - // Wait for timeout expiry - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - - // Next event should exit damping due to timeout - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_DOWN); - sendPortEvent(m_selectableChannel, portVid, SAI_PORT_OPER_STATUS_UP); - - std::this_thread::sleep_for(std::chrono::milliseconds(300)); -} diff --git a/unittest/syncd/TestSyncd.cpp b/unittest/syncd/TestSyncd.cpp index 3144cea4cf..95efccda07 100644 --- a/unittest/syncd/TestSyncd.cpp +++ b/unittest/syncd/TestSyncd.cpp @@ -21,6 +21,11 @@ #include #include +#include +#include + +#include "swss/table.h" + using namespace syncd; using namespace saivs; using namespace testing; @@ -772,4 +777,210 @@ TEST_F(SyncdTest, processEventInShutdownWaitMode_NonNotifyCommand) m_syncd->processEventInShutdownWaitMode(*channel); } + +// ---------------------------------------------------------------------------- +// Link event damping tests +// +// These tests exercise the real damping call chain: +// +// NotificationProcessor::syncProcessNotification +// -> handle_port_state_change +// -> process_on_port_state_change +// -> m_linkEventDampingApplier (bound to Syncd::applyLinkEventDamping) +// -> applyAiedAlgorithm / decayPenalty / writeDampingCountersToStateDb +// +// Notes: +// - Notifications must carry the port RID (the processor translates RID->VID). +// - Damping config is applied through Syncd::processEvent with op +// REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, which covers +// processLinkEventDampingConfigSet as well. +// - Results are asserted via the LINK_EVENT_DAMPING_STATS table in STATE_DB, +// which is written by writeDampingCountersToStateDb. +// ---------------------------------------------------------------------------- + +class SyncdLinkEventDampingTest : public SyncdTest +{ +protected: + static constexpr sai_object_id_t PORT_VID = 0x10000000000002; + static constexpr sai_object_id_t PORT_RID = 0x11000000000002; + + void SetUp() override + { + SyncdTest::SetUp(); + + m_syncd->m_translator->insertRidAndVid(PORT_RID, PORT_VID); + } + + // Apply damping config via the regular command path + // (covers Syncd::processLinkEventDampingConfigSet). + void setDampingConfig( + const sai_redis_link_event_damping_algo_aied_config_t& config) + { + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + + ":" + sai_serialize_object_id(PORT_VID); + + std::vector values = { + { sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM), + sai_serialize_redis_link_event_damping_algorithm(SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED) }, + { sai_serialize_redis_port_attr_id(SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG), + sai_serialize_redis_link_event_damping_aied_config(config) }, + }; + + swss::KeyOpFieldsValuesTuple kco(key, REDIS_ASIC_STATE_COMMAND_DAMPING_CONFIG_SET, values); + + MockSelectableChannel consumer; + EXPECT_CALL(consumer, empty()).WillOnce(testing::Return(true)); + EXPECT_CALL(consumer, pop(testing::_, testing::_)) + .WillOnce(testing::SetArgReferee<0>(kco)); + + m_syncd->processEvent(consumer); + } + + // Inject a port state change notification the same way the vendor SAI + // callback path would deliver it (using the port RID). + void sendPortStateChange( + sai_port_oper_status_t status) + { + sai_port_oper_status_notification_t n; + + memset(&n, 0, sizeof(n)); + + n.port_id = PORT_RID; // notification carries RID, processor translates to VID + n.port_state = status; + + std::string data = sai_serialize_port_oper_status_ntf(1, &n); + + std::vector entry; + swss::KeyOpFieldsValuesTuple item(SAI_SWITCH_NOTIFICATION_NAME_PORT_STATE_CHANGE, data, entry); + + m_syncd->m_processor->syncProcessNotification(item); + } + + // Read a damping counter field from STATE_DB written by + // writeDampingCountersToStateDb. + std::string getDampingField( + const std::string& field) + { + swss::DBConnector db("STATE_DB", 0); + swss::Table table(&db, "LINK_EVENT_DAMPING_STATS"); + + std::string value; + bool found = table.hget(sai_serialize_object_id(PORT_VID), field, value); + + return found ? value : ""; + } +}; + +TEST_F(SyncdLinkEventDampingTest, flapsEnterDampingAndSuppress) +{ + sai_redis_link_event_damping_algo_aied_config_t config; + config.max_suppress_time = 10000; + config.suppress_threshold = 1500; + config.reuse_threshold = 1000; + config.decay_half_life = 5000; + config.flap_penalty = 1000; + + setDampingConfig(config); + + // UNKNOWN -> DOWN: no penalty (penalty only on UP -> DOWN), propagated + sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); + // DOWN -> UP: propagated + sendPortStateChange(SAI_PORT_OPER_STATUS_UP); + // UP -> DOWN: penalty = 1000 < 1500, propagated + sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); + sendPortStateChange(SAI_PORT_OPER_STATUS_UP); + // UP -> DOWN: penalty ~2000 >= 1500 -> damping activated, + // threshold-crossing event itself is propagated + sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); + // damping active -> this UP must be suppressed + sendPortStateChange(SAI_PORT_OPER_STATUS_UP); + + EXPECT_EQ(getDampingField("is_damping_active"), "true"); + + // physical status followed the flaps, advertised stayed at last + // propagated state (DOWN), proving the UP was suppressed + EXPECT_EQ(getDampingField("physical_status"), "SAI_PORT_OPER_STATUS_UP"); + EXPECT_EQ(getDampingField("advertised_status"), "SAI_PORT_OPER_STATUS_DOWN"); + + // pre-damping counters see all transitions, post-damping counters + // miss the suppressed UP + EXPECT_EQ(getDampingField("pre_damping_down_events"), "3"); + EXPECT_EQ(getDampingField("pre_damping_up_events"), "3"); + EXPECT_EQ(getDampingField("post_damping_down_events"), "3"); + EXPECT_EQ(getDampingField("post_damping_up_events"), "2"); +} + +TEST_F(SyncdLinkEventDampingTest, maxSuppressTimeoutExitsDamping) +{ + sai_redis_link_event_damping_algo_aied_config_t config; + config.max_suppress_time = 200; // short timeout for the test + config.suppress_threshold = 100; + config.reuse_threshold = 50; + config.decay_half_life = 100; + config.flap_penalty = 1000; + + setDampingConfig(config); + + sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); + sendPortStateChange(SAI_PORT_OPER_STATUS_UP); + // UP -> DOWN: penalty 1000 >= 100 -> damping activated + sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); + // suppressed, physical becomes UP while advertised stays DOWN + sendPortStateChange(SAI_PORT_OPER_STATUS_UP); + + ASSERT_EQ(getDampingField("is_damping_active"), "true"); + + // wait past max_suppress_time + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + // same-state event (no DOWN, so damping timer is not reset): + // timeout branch in applyAiedAlgorithm exits damping and the event + // is propagated, synchronizing advertised with physical state + sendPortStateChange(SAI_PORT_OPER_STATUS_UP); + + EXPECT_EQ(getDampingField("is_damping_active"), "false"); + EXPECT_EQ(getDampingField("physical_status"), "SAI_PORT_OPER_STATUS_UP"); + EXPECT_EQ(getDampingField("advertised_status"), "SAI_PORT_OPER_STATUS_UP"); +} + +TEST_F(SyncdLinkEventDampingTest, penaltyDecayExitsDamping) +{ + sai_redis_link_event_damping_algo_aied_config_t config; + config.max_suppress_time = 10000; // large, so decay exit wins + config.suppress_threshold = 1000; + config.reuse_threshold = 600; + config.decay_half_life = 100; // fast decay + config.flap_penalty = 1000; + + setDampingConfig(config); + + sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); + sendPortStateChange(SAI_PORT_OPER_STATUS_UP); + // UP -> DOWN: penalty 1000 >= 1000 -> damping activated + sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); + // suppressed + sendPortStateChange(SAI_PORT_OPER_STATUS_UP); + + ASSERT_EQ(getDampingField("is_damping_active"), "true"); + + // wait ~3 half lives: penalty 1000 -> ~125 < reuse_threshold (600) + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + // same-state event triggers decayPenalty + reuse-threshold exit branch + sendPortStateChange(SAI_PORT_OPER_STATUS_UP); + + EXPECT_EQ(getDampingField("is_damping_active"), "false"); + EXPECT_EQ(getDampingField("advertised_status"), "SAI_PORT_OPER_STATUS_UP"); +} + +TEST_F(SyncdLinkEventDampingTest, noDampingConfiguredPropagates) +{ + // No damping config applied: applyLinkEventDamping returns early + // (port not in m_portLinkEventDampingStates) and events propagate + sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); + sendPortStateChange(SAI_PORT_OPER_STATUS_UP); + + // no STATE_DB entry is written for unconfigured ports + EXPECT_EQ(getDampingField("is_damping_active"), ""); +} #endif From 0cce42bd4d9acdbe61c0fff7f4ba013cf591ad52 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Thu, 11 Jun 2026 16:34:34 +0530 Subject: [PATCH 31/35] Trigger pipeline Signed-off-by: Sivakumar Thirukkanna Thevar From 8a2b883b1739819bf1bc19c10b191ba6bcb0153e Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Thu, 11 Jun 2026 18:32:25 +0530 Subject: [PATCH 32/35] Adding SWSS_LOG_ENTER() Signed-off-by: Sivakumar Thirukkanna Thevar --- unittest/syncd/TestSyncd.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/unittest/syncd/TestSyncd.cpp b/unittest/syncd/TestSyncd.cpp index 95efccda07..f7a99a1e1f 100644 --- a/unittest/syncd/TestSyncd.cpp +++ b/unittest/syncd/TestSyncd.cpp @@ -806,6 +806,8 @@ class SyncdLinkEventDampingTest : public SyncdTest void SetUp() override { + SWSS_LOG_ENTER(); + SyncdTest::SetUp(); m_syncd->m_translator->insertRidAndVid(PORT_RID, PORT_VID); @@ -816,6 +818,8 @@ class SyncdLinkEventDampingTest : public SyncdTest void setDampingConfig( const sai_redis_link_event_damping_algo_aied_config_t& config) { + SWSS_LOG_ENTER(); + std::string key = sai_serialize_object_type(SAI_OBJECT_TYPE_PORT) + ":" + sai_serialize_object_id(PORT_VID); @@ -841,6 +845,8 @@ class SyncdLinkEventDampingTest : public SyncdTest void sendPortStateChange( sai_port_oper_status_t status) { + SWSS_LOG_ENTER(); + sai_port_oper_status_notification_t n; memset(&n, 0, sizeof(n)); @@ -861,6 +867,8 @@ class SyncdLinkEventDampingTest : public SyncdTest std::string getDampingField( const std::string& field) { + SWSS_LOG_ENTER(); + swss::DBConnector db("STATE_DB", 0); swss::Table table(&db, "LINK_EVENT_DAMPING_STATS"); @@ -980,7 +988,7 @@ TEST_F(SyncdLinkEventDampingTest, noDampingConfiguredPropagates) sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); sendPortStateChange(SAI_PORT_OPER_STATUS_UP); - // no STATE_DB entry is written for unconfigured ports + // no STATE_DB entry is written for non-configured ports EXPECT_EQ(getDampingField("is_damping_active"), ""); } #endif From c59f6149b367d68425f3e44dafc6aedb78bae44a Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Thu, 11 Jun 2026 19:52:24 +0530 Subject: [PATCH 33/35] Debugging the test case Signed-off-by: Sivakumar Thirukkanna Thevar --- unittest/syncd/TestSyncd.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/unittest/syncd/TestSyncd.cpp b/unittest/syncd/TestSyncd.cpp index f7a99a1e1f..f077617150 100644 --- a/unittest/syncd/TestSyncd.cpp +++ b/unittest/syncd/TestSyncd.cpp @@ -968,6 +968,7 @@ TEST_F(SyncdLinkEventDampingTest, penaltyDecayExitsDamping) sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); // suppressed sendPortStateChange(SAI_PORT_OPER_STATUS_UP); + sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); ASSERT_EQ(getDampingField("is_damping_active"), "true"); From 55d92c323c31a8ec37d16bb3e7d1476c74d3240a Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Fri, 12 Jun 2026 06:13:48 +0530 Subject: [PATCH 34/35] Tweaking some timing value in one test Signed-off-by: Sivakumar Thirukkanna Thevar --- unittest/syncd/TestSyncd.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/unittest/syncd/TestSyncd.cpp b/unittest/syncd/TestSyncd.cpp index f077617150..492fd65c97 100644 --- a/unittest/syncd/TestSyncd.cpp +++ b/unittest/syncd/TestSyncd.cpp @@ -957,23 +957,25 @@ TEST_F(SyncdLinkEventDampingTest, penaltyDecayExitsDamping) config.max_suppress_time = 10000; // large, so decay exit wins config.suppress_threshold = 1000; config.reuse_threshold = 600; - config.decay_half_life = 100; // fast decay + config.decay_half_life = 200; // fast decay config.flap_penalty = 1000; setDampingConfig(config); sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); sendPortStateChange(SAI_PORT_OPER_STATUS_UP); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); // UP -> DOWN: penalty 1000 >= 1000 -> damping activated sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); // suppressed sendPortStateChange(SAI_PORT_OPER_STATUS_UP); - sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); ASSERT_EQ(getDampingField("is_damping_active"), "true"); // wait ~3 half lives: penalty 1000 -> ~125 < reuse_threshold (600) - std::this_thread::sleep_for(std::chrono::milliseconds(300)); + std::this_thread::sleep_for(std::chrono::milliseconds(600)); // same-state event triggers decayPenalty + reuse-threshold exit branch sendPortStateChange(SAI_PORT_OPER_STATUS_UP); From 6fc0dd435f0e218e7d296fa885b86f9a4fdd9754 Mon Sep 17 00:00:00 2001 From: Sivakumar Thirukkanna Thevar Date: Fri, 12 Jun 2026 07:01:48 +0530 Subject: [PATCH 35/35] Tweaking params Signed-off-by: Sivakumar Thirukkanna Thevar --- unittest/syncd/TestSyncd.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/unittest/syncd/TestSyncd.cpp b/unittest/syncd/TestSyncd.cpp index 492fd65c97..c041e29bcc 100644 --- a/unittest/syncd/TestSyncd.cpp +++ b/unittest/syncd/TestSyncd.cpp @@ -957,25 +957,22 @@ TEST_F(SyncdLinkEventDampingTest, penaltyDecayExitsDamping) config.max_suppress_time = 10000; // large, so decay exit wins config.suppress_threshold = 1000; config.reuse_threshold = 600; - config.decay_half_life = 200; // fast decay + config.decay_half_life = 1000; // fast decay config.flap_penalty = 1000; setDampingConfig(config); sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); sendPortStateChange(SAI_PORT_OPER_STATUS_UP); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); // UP -> DOWN: penalty 1000 >= 1000 -> damping activated sendPortStateChange(SAI_PORT_OPER_STATUS_DOWN); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); // suppressed sendPortStateChange(SAI_PORT_OPER_STATUS_UP); - ASSERT_EQ(getDampingField("is_damping_active"), "true"); + EXPECT_EQ(getDampingField("is_damping_active"), "true"); // wait ~3 half lives: penalty 1000 -> ~125 < reuse_threshold (600) - std::this_thread::sleep_for(std::chrono::milliseconds(600)); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // same-state event triggers decayPenalty + reuse-threshold exit branch sendPortStateChange(SAI_PORT_OPER_STATUS_UP);