Skip to content

[xcvrd] SI settings sync protocol between xcvrd and orchagent - #799

Open
arpit-nexthop wants to merge 10 commits into
sonic-net:masterfrom
nexthop-ai:si_notification_sync_master
Open

[xcvrd] SI settings sync protocol between xcvrd and orchagent#799
arpit-nexthop wants to merge 10 commits into
sonic-net:masterfrom
nexthop-ai:si_notification_sync_master

Conversation

@arpit-nexthop

Copy link
Copy Markdown
Contributor

What I did

Implements sonic-net/SONiC#2301

Description

Implements the xcvrd side of a redesigned SI settings synchronization protocol between xcvrd and orchagent. The previous design used a single NPU_SI_SETTINGS_SYNC_STATUS field in STATE_DB with values NPU_SI_SETTINGS_DEFAULT / _NOTIFIED / _DONE, with xcvrd writing directly to STATE_DB. This is replaced by a two-field, counter-based protocol.

Changes across files:

xcvr_table_helper.py

  • Removes NPU_SI_SETTINGS_SYNC_STATUS_KEY, NPU_SI_SETTINGS_DEFAULT_VALUE, NPU_SI_SETTINGS_NOTIFIED_VALUE constants
  • Adds a cached swsscommon.Table (app_port_read_tbl) for APPL_DB reads, created once per ASIC at init time — avoids creating a new Table object on every call since ProducerStateTable is write-only
  • Adds get_next_si_notification_number(port_name, asic_id): reads current si_sync_status from APPL_DB and increments its counter, returning 1 if absent or malformed. Increments from any : format (not just NOTIFIED) to handle the removal→re-insertion counter sequence correctly
  • Adds get_appl_db_port_table_val_by_key(lport, port_mapping, key): generic APPL_DB PORT_TABLE key reader using the cached table
  • Rewrites is_npu_si_settings_update_required(): now reads si_sync_status from APPL_DB instead of STATE_DB; returns False only if value is SI_SETTINGS_NOTIFIED:
  • Adds is_si_settings_already_applied(lport, port_mapping): verifies APPL_DB has SI_SETTINGS_NOTIFIED: and STATE_DB has SI_SYNC_DONE: with matching N — used by the restart-skip optimization
  • Removes dead get_state_db_port_table_val_by_key() (no production callers)

xcvrd.py

  • On SFP removal: writes si_sync_status = SI_SETTINGS_DEFAULT:<N+1> to APPL_DB PORT_TABLE (incrementing the counter) instead of writing NPU_SI_SETTINGS_DEFAULT to STATE_DB
  • on_add_logical_port: removes initialization of NPU_SI_SETTINGS_SYNC_STATUS in STATE_DB (orchagent now owns this initialization via initializePortSiSettingsSyncStatusBulk)
  • Removes initialize_port_init_control_fields_in_port_table() and its call in init() — entirely superseded by orchagent-side initialization

media_settings_parser.py

  • notify_media_setting(): appends si_sync_status = SI_SETTINGS_NOTIFIED: to the existing APPL_DB PORT_TABLE write (allocates one extra FieldValuePairs slot); removes the separate STATE_DB write of NPU_SI_SETTINGS_NOTIFIED

cmis_manager_task.py

  • Imports CMIS_STATE_SI_SETTINGS_WAIT (renamed from CMIS_STATE_NPU_SI_SETTINGS_WAIT)
  • Adds CMIS_SI_SETTINGS_WAIT_TIMEOUT = 10 seconds
  • Stores self.port_mapping in init (needed by helper methods)
  • New methods:
    • check_si_settings_app_status(lport): reads si_settings_sync_status from STATE_DB via hget
    • parse_si_notification_number(lport, si_sync_status): extracts from SI_SETTINGS_NOTIFIED:
    • check_si_sync_done_match(lport, si_settings_status, expected_number): validates STATE_DB SI_SYNC_DONE: matches the expected number; returns False (not True) when expected_number is None
    • can_skip_cmis_init_after_restart(lport, api, appl, host_lanes_mask): four-check restart optimization — application match on all active lanes, is_si_settings_already_applied, ConfigSuccess, DataPathActivated
  • handle_cmis_inserted_state(): calls can_skip_cmis_init_after_restart early; if all checks pass, transitions directly to CMIS_STATE_READY and posts active apsel, skipping CMIS reprogramming
  • process_cmis_state_machine() in CMIS_STATE_AP_CONF: after set_application, reads si_sync_status from APPL_DB; if SI_SETTINGS_NOTIFIED: is present, stores the notification number in port_dict and transitions to CMIS_STATE_SI_SETTINGS_WAIT; otherwise goes directly to CMIS_STATE_DP_INIT
  • New CMIS_STATE_SI_SETTINGS_WAIT handler: polls STATE_DB for SI_SYNC_DONE: matching the stored notification number; on match clears the number and transitions to CMIS_STATE_DP_INIT; on 10s timeout proceeds to CMIS_STATE_DP_INIT with a warning (no reinit)

common.py

  • Adds CMIS_STATE_SI_SETTINGS_WAIT = 'SI_SETTINGS_WAIT' constant

Motivation and Context

The old design had two problems:

  1. xcvrd wrote NPU_SI_SETTINGS_NOTIFIED directly to STATE_DB, which is not read by orchagent, which only subscribes the APPL_DB
  2. CMIS state machine is not waiting for the SI settings to be applied on the ASIC/gearbox before going on with the datapath programming.

How Has This Been Tested?

Unit tests added/updated in test_xcvrd.py:

  • test_get_next_si_notification_number: increments from SI_SETTINGS_DEFAULT, SI_SETTINGS_NOTIFIED, absent, and malformed formats
  • test_get_appl_db_port_table_val_by_key: None asic_index, port not found, key absent, key present — using cached table directly
  • test_is_npu_si_settings_update_required: None, DEFAULT, NOTIFIED, and unknown format inputs
  • test_is_si_settings_already_applied: full matrix of None APPL_DB status, DEFAULT, malformed number, None asic_index, STATE_DB not found, wrong status, number mismatch, and matching numbers
  • test_can_skip_cmis_init_after_restart: application mismatch, SI not synced, config not success, datapath not activated, all pass
  • test_on_sfp_remove_event_resets_si_sync_status: verifies APPL_DB write of SI_SETTINGS_DEFAULT: on SFP removal
  • Removed test_DaemonXcvrd_initialize_port_init_control_fields_in_port_table (function deleted)
  • CMIS SM task worker tests: set APPL_DB read mock to return not-found so state bypasses CMIS_STATE_SI_SETTINGS_WAIT and goes directly to CMIS_STATE_DP_INIT

Tests done on device:

  • shut / no shut tests: The notification count does not increase after 1.
  • xcvr removal and insertion tests: Checks for the programming of the SI settings and increase in the notification number
  • xcvrd restart test: The transceiver was not programmed again.

Additional Information (Optional)

@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@arpit-nexthop arpit-nexthop changed the title SI settings sync protocol between xcvrd and orchagent [xcvrd] SI settings sync protocol between xcvrd and orchagent Apr 20, 2026
@rlhui
rlhui requested review from bmridul and mihirpat1 April 22, 2026 17:24
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command.

@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates sonic-xcvrd to support a redesigned SI settings synchronization protocol with orchagent, shifting from the legacy STATE_DB single-field status to an APPL_DB counter-based notification flow and adding a CMIS state-machine wait point for SI application acknowledgment.

Changes:

  • Add an APPL_DB read path (cached Table) and a helper to compute the next SI notification counter for si_settings_notification.
  • Update SI settings publishing to include si_settings_notification=SI_SETTINGS_NOTIFIED:<N> in APPL_DB, and reset to SI_SETTINGS_DEFAULT:<N> on SFP removal.
  • Extend the CMIS state machine with CMIS_STATE_SI_SETTINGS_WAIT and logic to wait for a corresponding SI_SYNC_DONE:<N> ack in STATE_DB.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
sonic-xcvrd/xcvrd/xcvrd.py Removes prior direct SI notify calls and switches SFP-removal reset to APPL_DB si_settings_notification counter format; adjusts SFF manager construction.
sonic-xcvrd/xcvrd/xcvrd_utilities/xcvr_table_helper.py Adds cached APPL_DB PORT_TABLE reader and helper to compute the next notification number.
sonic-xcvrd/xcvrd/xcvrd_utilities/media_settings_parser.py Adds si_settings_notification=SI_SETTINGS_NOTIFIED:<N> to the APPL_DB write and returns the notification number.
sonic-xcvrd/xcvrd/xcvrd_utilities/common.py Adds CMIS_STATE_SI_SETTINGS_WAIT constant.
sonic-xcvrd/xcvrd/sff_mgr.py Triggers SI settings notification on TRANSCEIVER_INFO updates (for non-CMIS paths).
sonic-xcvrd/xcvrd/cmis/cmis_manager_task.py Adds SI settings wait state and notification number tracking for CMIS ports.
sonic-xcvrd/tests/test_xcvrd.py Updates/extends unit tests for the new counter-based protocol and new CMIS SI-wait state.


if rc != SFP_EEPROM_NOT_READY:
self.dom_db_utils.post_port_dom_thresholds_to_db(logical_port)
self.vdm_db_utils.post_port_vdm_thresholds_to_db(logical_port)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added notify_media_setting_on_insert() at all insert sites; publishes for non-CMIS independent of --enable_sff_mgr, no-ops for CMIS/synced.

Comment on lines +1205 to +1209
# Check if SI settings notification number was cached during INSERTED state
notification_number = self.port_dict[lport].get('si_notification_number')

if notification_number is not None:
self.log_notice("{}: SI settings notified to OA with number {}, waiting for ASIC to apply".format(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cached si_notification_number is now authoritative at AP_CONF because it's recovered earlier: a restart always re-enters CMIS_STATE_INSERTED (via force_cmis_reinit), and AP_CONF is strictly downstream of INSERTED.

Comment on lines +1246 to +1250
self.log_notice("{}: timeout waiting for SI settings to be applied (status: {}, expected: {})".format(
lport, si_settings_status, expected_notification_number))
self.port_dict[lport].pop('si_notification_number', None)
self.port_dict[lport]['notify_si_settings'] = True
self.force_cmis_reinit(lport, retries + 1)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix the PR description

Comment thread sonic-xcvrd/xcvrd/xcvrd.py Outdated
Comment on lines 786 to 790
state_port_table = self.xcvr_table_helper.get_state_port_tbl(port_change_event.asic_id)
found, state_port_table_fvs = state_port_table.get(port_change_event.port_name)
if not found:
helper_logger.log_notice("Add logical port: Creating STATE_DB PORT_TABLE as unable to find for lport {}".format(port_change_event.port_name))
state_port_table_fvs = []

CMIS_MAX_RETRIES = 3
CMIS_DEF_EXPIRED = 60 # seconds, default expiration time
CMIS_SI_SETTINGS_WAIT_TIMEOUT = 10 # seconds, timeout for waiting SI settings to be applied on ASIC

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arpit-nexthop Please move this common.py

@mihirpat1 mihirpat1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_next_si_notification_number can return the same N twice due to producer/consumer lag

Reads and writes go through different swsscommon objects on different tables:

  • Writes use app_port_tbl (ProducerStateTable) - entries land in _PORT_TABLE and are only moved into PORT_TABLE after orchagent's ConsumerStateTable drains them.
  • get_next_si_notification_number reads app_port_read_tbl (plain Table on PORT_TABLE) - i.e. the post-consumer view.

So two back-to-back calls (e.g. an initial notify followed shortly by a force_cmis_reinit on SI_SETTINGS_WAIT timeout) can both observe the same pre-write value and return the same N:

notify_media_setting() -> reads N=5 from PORT_TABLE -> writes :6 to _PORT_TABLE
SI_SETTINGS_WAIT timeout -> force_cmis_reinit
notify_media_setting() -> PORT_TABLE still shows :5 (OA hasn't drained) -> returns 6 again

This breaks the "monotonically increasing counter" invariant the HLD relies on, and can_skip_cmis_init_after_restart may then match a stale SI_SYNC_DONE:N from the first notification while the second is still pending. The window widens exactly when it matters most - high port-count startup or right after a SI_SETTINGS_WAIT timeout, since OA being slow is what caused the timeout in the first place.

@mihirpat1

Copy link
Copy Markdown
Contributor

@arpit-nexthop Please fix the build failure

Comment thread sonic-xcvrd/xcvrd/xcvrd_utilities/media_settings_parser.py
helper_logger.log_notice("Notify media setting: Published SI setting "
"for lport {} in APP_DB".format(logical_port_name))
"for lport {} in APP_DB with notification number {}".format(logical_port_name, notification_number))
last_notification_number = notification_number

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not consuming return value from notify_media_settings anywhere. Is it necessary for us to return last_notification_number here? If not, we can remove the use of this variable?

If it's necessary can't we just do return notification_number in the previous block itself?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return value is consumed by handle_cmis_inserted_state, keeping this form so early returns still give None

Comment thread sonic-xcvrd/xcvrd/xcvrd_utilities/xcvr_table_helper.py Outdated
Comment thread sonic-xcvrd/xcvrd/xcvrd_utilities/xcvr_table_helper.py Outdated
Comment thread sonic-xcvrd/xcvrd/xcvrd_utilities/xcvr_table_helper.py Outdated
return port_speed, lane_count, subport_num



Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Remove this newline

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread sonic-xcvrd/xcvrd/cmis/cmis_manager_task.py Outdated
Comment thread sonic-xcvrd/tests/test_xcvrd.py
@arpit-nexthop

Copy link
Copy Markdown
Contributor Author

@arpit-nexthop, We have now merged this PR and its sister PR SWSS #4497 to master branch in our environment, and we are attempting to run OC tests. First problem is that the Inband ports are not coming oper up because admin up notification is never sent to SAI. Relevant log output is pasted below (see specifically timestamp 01:23:58.870282).

Have you guys run OC tests on these changes?

I have posted same comment at PR SWSS #4497.

2026 Aug 10 01:14:44.138168 ixre-egl-board201 DEBUG bgp0#bgpcfgd: Received message : '('Ethernet-IB0|3333::3:1/128', 'SET', (('NULL', 'NULL'),))'
2026 Aug 10 01:22:20.722348 ixre-egl-board201 NOTICE swss0#portsyncd: :- onMsg: nlmsg type:16 key:Ethernet-IB0 admin:0 oper:0 addr:18:c3:00:8d:f1:ee ifindex:90 master:0 flags:4098
2026 Aug 10 01:22:20.722678 ixre-egl-board201 NOTICE swss0#portsyncd: :- onMsg: nlmsg type:16 key:Ethernet-IB0 admin:0 oper:0 addr:18:c3:00:8d:f1:ee ifindex:90 master:0 flags:4098
2026 Aug 10 01:22:21.901451 ixre-egl-board201 DEBUG bgp0#bgpcfgd: Received message : '('Ethernet-IB0|3.3.3.1/32', 'SET', (('NULL', 'NULL'),))'
2026 Aug 10 01:22:21.901566 ixre-egl-board201 DEBUG bgp0#bgpcfgd: Received message : '('Ethernet-IB0', 'SET', (('inband_type', 'port'),))'
2026 Aug 10 01:22:21.901609 ixre-egl-board201 DEBUG bgp0#bgpcfgd: Received message : '('Ethernet-IB0|3333::3:1/128', 'SET', (('NULL', 'NULL'),))'
2026 Aug 10 01:22:29.926826 ixre-egl-board201 DEBUG bgp0#bgpcfgd: Received message : '('Ethernet-IB0', 'SET', (('vrf', ''),))'
2026 Aug 10 01:23:15.040076 ixre-egl-board201 NOTICE swss0#portsyncd: :- onMsg: nlmsg type:17 key:Ethernet-IB0 admin:0 oper:0 addr:18:c3:00:8d:f1:ee ifindex:90 master:0 flags:4098
2026 Aug 10 01:23:50.980935 ixre-egl-board201 NOTICE macsec0#macsecmgrd: :- disableMACsec: The MACsec was not enabled on the port 'Ethernet-IB0'
2026 Aug 10 01:23:51.870541 ixre-egl-board201 NOTICE swss0#orchagent: :- setPortSiSettingsSyncStatus: Set port Ethernet-IB0 SI settings sync status to SI_SETTINGS_DEFAULT:0
2026 Aug 10 01:23:51.870546 ixre-egl-board201 NOTICE swss0#orchagent: :- initializePortSiSettingsSyncStatusBulk: Initialize si_settings_ack as SI_SETTINGS_DEFAULT:0 for port Ethernet-IB0
2026 Aug 10 01:23:51.871263 ixre-egl-board201 NOTICE pmon#ledd[37]: Received PORT table event: key=Ethernet-IB0, state=down
2026 Aug 10 01:23:51.872404 ixre-egl-board201 NOTICE swss0#portsyncd: :- onMsg: nlmsg type:16 key:Ethernet-IB0 admin:0 oper:0 addr:18:c3:00:8d:f1:ee ifindex:119 master:0 flags:4098
2026 Aug 10 01:23:51.872665 ixre-egl-board201 NOTICE swss0#orchagent: :- addHostIntfs: Create host interface for port Ethernet-IB0 with oper status down
2026 Aug 10 01:23:51.872718 ixre-egl-board201 NOTICE swss0#orchagent: :- publish: EVENT_PUBLISHED: {"sonic-events-swss:if-state":{"ifname":"Ethernet-IB0","status":"down","timestamp":"2026-08-10T01:23:51.872676Z"}}
2026 Aug 10 01:23:51.872747 ixre-egl-board201 NOTICE swss0#portsyncd: :- onMsg: Publish Ethernet-IB0(ok:down) to state db
2026 Aug 10 01:23:51.874415 ixre-egl-board201 NOTICE swss0#orchagent: :- initPortsBulk: Initialized port Ethernet-IB0
2026 Aug 10 01:23:51.871504 ixre-egl-board201 NOTICE pmon#ledd[37]: Received PORT table event: key=Ethernet-IB0, state=down
2026 Aug 10 01:23:51.880763 ixre-egl-board201 NOTICE swss0#orchagent: :- setRouterIntfsMtu: Update MTU to 9100 for all router interfaces mapped to port: Ethernet-IB0
2026 Aug 10 01:23:51.880770 ixre-egl-board201 NOTICE swss0#orchagent: :- doPortTask: Set port Ethernet-IB0 MTU to 9100
2026 Aug 10 01:23:51.880776 ixre-egl-board201 NOTICE swss0#orchagent: :- doPortTask: Port Ethernet-IB0: deferring admin-up until SI settings are notified
2026 Aug 10 01:23:51.880892 ixre-egl-board201 NOTICE swss0#orchagent: :- setHostTxReady: Setting host_tx_ready status = false, alias = Ethernet-IB0, port_id = 0x1000000000201
2026 Aug 10 01:23:51.881023 ixre-egl-board201 NOTICE swss0#orchagent: :- initHostTxReadyState: initialize host_tx_ready as false for port Ethernet-IB0
2026 Aug 10 01:23:51.977441 ixre-egl-board201 NOTICE swss0#orchagent: :- addSystemPorts: Added system port 5d00000000006a for ixre-egl-board202|asic0|Ethernet-IB0
2026 Aug 10 01:23:51.987263 ixre-egl-board201 NOTICE swss0#orchagent: :- addSystemPorts: Added system port 5d000000000036 for ixre-egl-board201|asic0|Ethernet-IB0
2026 Aug 10 01:23:52.889775 ixre-egl-board201 NOTICE swss0#portmgrd: :- doTask: Configure Ethernet-IB0 MTU to 9100
2026 Aug 10 01:23:52.893911 ixre-egl-board201 NOTICE swss0#portsyncd: :- onMsg: nlmsg type:16 key:Ethernet-IB0 admin:1 oper:0 addr:18:c3:00:8d:f1:ee ifindex:119 master:0 flags:4099
2026 Aug 10 01:23:52.894533 ixre-egl-board201 NOTICE swss0#portsyncd: :- onMsg: Publish Ethernet-IB0(ok:down) to state db
2026 Aug 10 01:23:52.895114 ixre-egl-board201 NOTICE swss0#portmgrd: :- doTask: Configure Ethernet-IB0 admin status to up
2026 Aug 10 01:23:53.934628 ixre-egl-board201 NOTICE swss0#orchagent: :- addRouterIntfs: Create router interface Ethernet-IB0 MTU 9100
2026 Aug 10 01:23:55.019746 ixre-egl-board201 DEBUG bgp0#bgpcfgd: Received message : '('Ethernet-IB0|3.3.3.1/32', 'SET', (('state', 'ok'),))'
2026 Aug 10 01:23:55.023811 ixre-egl-board201 DEBUG bgp0#bgpcfgd: Received message : '('Ethernet-IB0|3333::3:1/128', 'SET', (('state', 'ok'),))'
2026 Aug 10 01:23:58.813371 ixre-egl-board201 NOTICE swss0#orchagent: :- addInbandNeighbor: Created inband neighbor 18:c3:00:8d:f1:ee on Ethernet-IB0
2026 Aug 10 01:23:58.817592 ixre-egl-board201 NOTICE swss0#orchagent: :- addInbandNeighbor: Created inband neighbor 18:c3:00:8d:f1:ee on Ethernet-IB0
2026 Aug 10 01:23:58.870282 ixre-egl-board201 NOTICE swss0#orchagent: :- doPortTask: Port Ethernet-IB0: deferring admin-up until SI settings are notified
2026 Aug 10 01:23:58.872848 ixre-egl-board201 NOTICE pmon#ledd[37]: Received PORT table event: key=Ethernet-IB0, state=down
2026 Aug 10 01:23:58.872908 ixre-egl-board201 NOTICE pmon#ledd[37]: Received PORT table event: key=Ethernet-IB0, state=down
2026 Aug 10 01:24:07.032595 ixre-egl-board201 NOTICE swss1#orchagent: :- addSystemPorts: Added system port 15d00000000006a for ixre-egl-board202|asic0|Ethernet-IB0
2026 Aug 10 01:24:07.109942 ixre-egl-board201 NOTICE swss1#orchagent: :- addSystemPorts: Added system port 15d000000000055 for ixre-egl-board201|asic0|Ethernet-IB0
2026 Aug 10 01:24:09.151645 ixre-egl-board201 NOTICE swss1#orchagent: :- addRouterIntfs: Create router interface ixre-egl-board201|asic0|Ethernet-IB0 MTU 9100
2026 Aug 10 01:24:22.053109 ixre-egl-board201 NOTICE swss0#orchagent: :- addRouterIntfs: Create router interface ixre-egl-board202|asic0|Ethernet-IB0 MTU 9100
2026 Aug 10 01:24:22.054334 ixre-egl-board201 NOTICE swss1#orchagent: :- addRouterIntfs: Create router interface ixre-egl-board202|asic0|Ethernet-IB0 MTU 9100``` 

@snider-nokia
I have been testing it, our internal branches are not exactly 1:1 with master, I will check if some patch got missed.

lport, si_settings_status, e))
return False

def can_skip_cmis_init_after_restart(self, lport, api, appl, host_lanes_mask):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should already be solved, check it

Comment on lines +1246 to +1250
self.log_notice("{}: timeout waiting for SI settings to be applied (status: {}, expected: {})".format(
lport, si_settings_status, expected_notification_number))
self.port_dict[lport].pop('si_notification_number', None)
self.port_dict[lport]['notify_si_settings'] = True
self.force_cmis_reinit(lport, retries + 1)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix the PR description

Comment thread sonic-xcvrd/xcvrd/cmis/cmis_manager_task.py Outdated
Comment thread sonic-xcvrd/tests/test_xcvrd.py
Comment thread sonic-xcvrd/xcvrd/cmis/cmis_manager_task.py
Comment on lines +1205 to +1209
# Check if SI settings notification number was cached during INSERTED state
notification_number = self.port_dict[lport].get('si_notification_number')

if notification_number is not None:
self.log_notice("{}: SI settings notified to OA with number {}, waiting for ASIC to apply".format(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cached si_notification_number is now authoritative at AP_CONF because it's recovered earlier: a restart always re-enters CMIS_STATE_INSERTED (via force_cmis_reinit), and AP_CONF is strictly downstream of INSERTED.


if self.XCVR_TYPE in port_change_event.port_dict:
self.port_dict[lport][self.XCVR_TYPE] = port_change_event.port_dict[self.XCVR_TYPE]
self.port_dict[lport]['notify_si_settings'] = True

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SFF publish guarded by is_si_settings_synced(); replayed TRANSCEIVER_INFO won't re-notify a synced module.

self.log_notice("{}: timeout waiting for SI settings to be applied (status: {}, expected: {})".format(
lport, si_settings_status, expected_notification_number))
self.port_dict[lport].pop('si_notification_number', None)
self.port_dict[lport]['notify_si_settings'] = True

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the DEFAULT:<N+1> write via shared reset_si_settings_notification_to_default().

return port_speed, lane_count, subport_num



Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


if rc != SFP_EEPROM_NOT_READY:
self.dom_db_utils.post_port_dom_thresholds_to_db(logical_port)
self.vdm_db_utils.post_port_vdm_thresholds_to_db(logical_port)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added notify_media_setting_on_insert() at all insert sites; publishes for non-CMIS independent of --enable_sff_mgr, no-ops for CMIS/synced.

…ync_master

Signed-off-by: arpit-nexthop <arpit@nexthop.ai>

# Conflicts:
#	sonic-xcvrd/tests/test_xcvrd.py
#	sonic-xcvrd/xcvrd/cmis/cmis_manager_task.py
#	sonic-xcvrd/xcvrd/xcvrd.py
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@rlhui

rlhui commented Aug 19, 2026

Copy link
Copy Markdown

@mihirpat1 - please re-review if all comments are addressed?

The CPO refactor (sonic-net#843) changed CmisManagerTask.__init__ to take port_obj_dict
positionally and dropped the platform_chassis kwarg. Update the two SI-sync tests
(notify-arming, SI_SETTINGS_WAIT) that still used the old signature.

Signed-off-by: arpit-nexthop <arpit@nexthop.ai>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Cover the SI-sync helpers/paths previously only exercised through mocks:
- check_si_sync_done_match: all branches
- check_si_settings_ack_status: ack present / absent
- notify_si_settings_unavailable early-return guards
- is_si_settings_synced malformed notification/ack branches
- notify_media_setting_on_insert CMIS / SFF-enabled / xcvr_info-None / api-error branches
- handle_cmis_inserted_state restart recovery (already-synced skip, adopt-outstanding)

Signed-off-by: arpit-nexthop <arpit@nexthop.ai>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

prefix, count = ack.split(':', 1)
# OA acks either SI_SYNC_DONE:<N> (real SI applied) or SI_SETTINGS_DEFAULT:<N>
# (defaults applied); both mean notification <N> has been fully processed.
return prefix in ('SI_SYNC_DONE', 'SI_SETTINGS_DEFAULT') and int(count) == notified

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High: SI_SETTINGS_DEFAULT:<N> is not proof that SI settings for the current module were applied

get_current_si_notification_number() discards the APPL_DB notification type and returns only N; this function then accepts either SI_SYNC_DONE:N or SI_SETTINGS_DEFAULT:N as synchronized. Those states have different meanings. DONE:N means OA applied a NOTIFIED:N request. DEFAULT:N is the removal/reset handshake; OA's DEFAULT path does not program new-module SerDes settings, it only echoes the DEFAULT ack.

A normal OIR sequence can therefore produce a false positive:

old module removed
APPL_DB  = SI_SETTINGS_DEFAULT:8
STATE_DB = SI_SETTINGS_DEFAULT:8
new module inserted
is_si_settings_synced() returns True from matching number 8
SFF/direct path skips notification, or CMIS marks SI synced
new module's SI settings are never published/applied

There is a second ordering failure: if insertion observes DEFAULT:8 before OA has acknowledged it, CMIS treats the bare number as an outstanding SI notification and waits for SI_SYNC_DONE:8; OA will only answer SI_SETTINGS_DEFAULT:8, causing timeout/reinit instead of a fresh notification.

Shall we preserve and validate both type and counter? Restart-skip should require the exact pair APPL_DB = SI_SETTINGS_NOTIFIED:N and STATE_DB = SI_SYNC_DONE:N. Only NOTIFIED:N should be considered outstanding. DEFAULT:N must force a fresh SI decision on the next insertion, while UNAVAIL remains a separate terminal no-settings result. The tests should reject DEFAULT:N + DEFAULT:N as applied SI and cover removal followed immediately by insertion.

Comment thread sonic-xcvrd/xcvrd/xcvrd.py Outdated
api = None
# CMIS modules are handled by CmisManagerTask, and SFF modules by SffManagerTask
# when it is enabled; only publish here for a non-CMIS module with the SFF manager off.
if common.is_cmis_api(api) or self.enable_sff_mgr:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High regression: the new SI publisher split leaves valid CMIS and retry paths without a terminal notification

This is broader than the non-CMIS ownership issue discussed in #799 (comment). The added fallback covers non-CMIS modules when SFF manager is disabled, but the implementation still has paths for which no component owns SI publication.

Required invariant

For every present external port whose admin-up can be gated by OA, the insertion flow needs one responsible owner that eventually publishes one of these terminal results:

  • SI_SETTINGS_NOTIFIED:N when settings must be applied.
  • SI_SETTINGS_UNAVAIL when there are no applicable/matching settings.

The current ownership split violates that invariant in several distinct cases:

  • Flat-memory/non-paged CMIS: the direct SfpStateUpdateTask fallback returns for every common.is_cmis_api(api); SffManagerTask also skips every CMIS API; and CmisManagerTask sets flat-memory CMIS to READY and returns. Therefore none of the three paths publishes NOTIFIED:N or UNAVAIL. Flat-memory CMIS, including passive-copper CMIS, can still require platform-specific NPU SI based on cable length, lane speed, board trace, and ASIC.
  • CMIS with --skip_cmis_mgr: the CMIS task does not exist, but the always-on fallback still skips all CMIS APIs. This is a supported configuration, not an error path, and it has no SI publisher.
  • Transient publisher-side information-read failure: if the second get_transceiver_info() call below returns None, this path returns without adding the port back to the EEPROM retry set. The SFF and CMIS paths can similarly consume/clear their one-shot notify_si_settings flag before the publisher read succeeds. The initial EEPROM-ready retry does not guarantee a retry of this later read, so a temporary failure can become a permanent missing notification.

This is a behavioral regression. Before this PR, the always-on SfpStateUpdateTask directly called media_settings_parser.notify_media_setting() after successful normal EEPROM reads, including for flat-memory CMIS. The HLD defines a CMIS-state-machine-driven module as CMIS and not flat-memory; using is_cmis_api() as the ownership boundary is therefore too broad.

The resulting failure is:

external port is configured admin-up
        -> OA defers NPU admin-up while waiting for SI
        -> selected xcvrd path publishes neither NOTIFIED:N nor UNAVAIL
        -> OA never releases the gate
        -> the configured-up port remains down indefinitely

Shall we assign an explicit owner to every path: paged CMIS with the manager enabled can remain owned by CmisManagerTask; flat-memory CMIS and CMIS when the manager is intentionally skipped need the always-on/direct publisher. A successful decision must produce NOTIFIED:N or UNAVAIL, while transient read failures must rearm/retry rather than consume the event. Please also ensure paged CMIS has only one publisher.

Tests should cover flat-memory CMIS copper, flat-memory CMIS with no matching media entry, --skip_cmis_mgr, transient information-read failure followed by recovery, and no double-publication for paged CMIS.

self.app_port_read_tbl = {}
# (asic_id, port_name) -> last SI notification number issued this process
# (keeps get_next_si_notification_number monotonic despite the PORT_TABLE read lag).
self.last_si_notification_number = {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: SI notification-number allocation is not serialized across xcvrd task owners

N is effectively a transaction ID. It lets OA say, "I processed exactly notification N," rather than acknowledging an older optic's settings.

Several threads can allocate it:

Task Notification
SfpStateUpdateTask DEFAULT:N on removal
CmisManagerTask NOTIFIED:N on insertion; DEFAULT:N on timeout
SffManagerTask NOTIFIED:N on insertion

Each task constructs a separate XcvrTableHelper, with its own last_si_notification_number cache. Allocation performs:

read current N from APPL_DB
next = max(database N, this helper's local N) + 1
update this helper's local cache
later publish the notification

Normal sequential processing is safe because the APPL_DB write is synchronous:

SfpStateUpdateTask processes removal and publishes DEFAULT:10
          |
          v
CmisManagerTask processes a later insertion and reads 10
          |
          v
publishes NOTIFIED:11

The problem occurs when processing for the same physical module overlaps. Here module A is current when CMIS processing begins and becomes the old/removed optic during the race:

1. Module A is inserted.
2. SfpStateUpdateTask processes SFP_STATUS_INSERTED and publishes A's TRANSCEIVER_INFO.
3. CmisManagerTask observes that update and starts processing CMIS_STATE_INSERTED for A.
4. CmisManagerTask reads APPL_DB N=10, allocates N=11, and prepares A's SI payload.
5. Module A is quickly removed before that payload is published.
6. SfpStateUpdateTask processes SFP_STATUS_REMOVED, also reads N=10 through its
   separate helper, allocates N=11, and publishes DEFAULT:11.
7. CmisManagerTask's unfinished CMIS_STATE_INSERTED work for A resumes and publishes
   A's stale SI payload as NOTIFIED:11.
8. Module B may subsequently be inserted into the same port.

The overlapping allocation looks like this:

APPL_DB currently has N=10

CmisManagerTask processing A's         SfpStateUpdateTask processing A's
CMIS_STATE_INSERTED                    later SFP_STATUS_REMOVED event
--------------------------------       ----------------------------------
reads N=10
allocates N=11
prepares A's SI payload
                                       reads N=10
                                       allocates N=11
                                       publishes DEFAULT:11
publishes stale NOTIFIED:11
                                       |
                                       v
                            Final APPL_DB = NOTIFIED:11

OA can then apply tuning captured for removed module A and acknowledge DONE:11. On module B's insertion or an xcvrd restart, retained APPL_DB = NOTIFIED:11 plus STATE_DB = DONE:11 looks perfectly synchronized, even though it belongs to the wrong module generation. This remains possible even after fixing the separate DEFAULT:N matching issue.

Could SI publication be made single-owner per port, or protected by a shared per-port lock/generation mechanism covering presence revalidation, number allocation, and the APPL_DB write? Atomic N allocation alone would guarantee unique numbers but would not stop stale work for module A from publishing after its removal. A barrier-based test that forces CMIS_STATE_INSERTED notification to overlap SFP_STATUS_REMOVED would make this behavior deterministic.

nh-grecs Bot pushed a commit to nexthop-ai/sonic-platform-daemons that referenced this pull request Aug 23, 2026
rebuild-source: sonic-net#799 @ nexthop-ai/sonic-platform-daemons d80bfce [case: upstream:open]
nh-grecs Bot pushed a commit to nexthop-ai/sonic-platform-daemons that referenced this pull request Aug 24, 2026
rebuild-source: sonic-net#799 @ nexthop-ai/sonic-platform-daemons d80bfce [case: upstream:open]
nh-grecs Bot pushed a commit to nexthop-ai/sonic-platform-daemons that referenced this pull request Aug 25, 2026
rebuild-source: sonic-net#799 @ nexthop-ai/sonic-platform-daemons d80bfce [case: upstream:open]
…sitive)

is_si_settings_synced accepted SI_SETTINGS_DEFAULT:<N> as applied SI, so a
remove->insert (OIR) with matching counters falsely reported synced and the new
module's SI was never published. Make the notification type significant:

- Synced requires APPL_DB SI_SETTINGS_NOTIFIED:<N> AND STATE_DB SI_SYNC_DONE:<N>.
- Only SI_SETTINGS_NOTIFIED:<N> is 'outstanding' (get_current -> get_outstanding);
  DEFAULT:<N>/UNAVAIL force a fresh SI decision on the next insertion.
- Add _parse_si_value helper; tests reject DEFAULT+DEFAULT and cover OIR.

Addresses review comment on is_si_settings_synced (DEFAULT:N false positive).

Signed-off-by: arpit-nexthop <arpit@nexthop.ai>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

notify_media_setting_on_insert used is_cmis_api() as the ownership boundary, which is
too broad: flat-memory CMIS (incl. passive copper) and CMIS under --skip_cmis_mgr had no
publisher (CmisManagerTask skips flat memory / does not run), so orchagent held admin-up
forever waiting for an SI notification.

Own here everything CmisManagerTask/SffManagerTask do not: publish unless the module is
paged CMIS with the manager running, or non-CMIS with --enable_sff_mgr. Also re-arm the
EEPROM retry on a transient get_transceiver_info() failure. Rework the test to cover
paged/flat/skip_cmis_mgr ownership and the transient re-arm.

Addresses review comment on the SI-publisher ownership split.

Signed-off-by: arpit-nexthop <arpit@nexthop.ai>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@judyjoseph

Copy link
Copy Markdown
Contributor

@anamehra @snider-nokia @mihirpat1 to review again + @arpit-nexthop can you get the sonic-mgmt testrun and share results PR description

nh-grecs Bot pushed a commit to nexthop-ai/sonic-platform-daemons that referenced this pull request Aug 26, 2026
rebuild-source: sonic-net#799 @ nexthop-ai/sonic-platform-daemons e1989c0 [case: upstream:open]
nh-grecs Bot pushed a commit to nexthop-ai/sonic-platform-daemons that referenced this pull request Aug 27, 2026
rebuild-source: sonic-net#799 @ nexthop-ai/sonic-platform-daemons e1989c0 [case: upstream:open]
nh-grecs Bot pushed a commit to nexthop-ai/sonic-platform-daemons that referenced this pull request Aug 27, 2026
rebuild-source: sonic-net#799 @ nexthop-ai/sonic-platform-daemons e1989c0 [case: upstream:open]
nh-grecs Bot pushed a commit to nexthop-ai/sonic-platform-daemons that referenced this pull request Aug 28, 2026
rebuild-source: sonic-net#799 @ nexthop-ai/sonic-platform-daemons e1989c0 [case: upstream:open]
nh-grecs Bot pushed a commit to nexthop-ai/sonic-platform-daemons that referenced this pull request Aug 29, 2026
rebuild-source: sonic-net#799 @ nexthop-ai/sonic-platform-daemons e1989c0 [case: upstream:open]
nh-grecs Bot pushed a commit to nexthop-ai/sonic-platform-daemons that referenced this pull request Aug 29, 2026
rebuild-source: sonic-net#799 @ nexthop-ai/sonic-platform-daemons e1989c0 [case: upstream:open]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.