From 3d1228a8c0622d6ac1d0d465d159f4d9ed5943c4 Mon Sep 17 00:00:00 2001 From: Bohan Yang <105526392+abohanyang@users.noreply.github.com> Date: Thu, 20 Oct 2022 09:39:38 -0700 Subject: [PATCH] Fix xcvrd to support 400G ZR optic (#293) * Fix xcvrd to support 400G ZR/DR optics * Fix xcvrd to support 400G ZR/DR optics * Revert changes in DomInfoUpdateTask::on_port_config_change * Fix xcvrd test after modifying on_port_config_change * Call get_datapath_init_duration to get the init expiration time. * Address comments 1. Clean up comments. 2. Revert port breakout fix and corresponding test change. 3. Check deinit duration for deinit case 4. Check datapath init pending * 1. Revert changes in on_port_update_event 2. check DpInitPending on module which supports CMIS 5.0 3. Specify sec in return value * Add code coverage * Add log for DpInit/DpDeinit duration --- sonic-xcvrd/tests/test_xcvrd.py | 13 +++++++++ sonic-xcvrd/xcvrd/xcvrd.py | 51 +++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/sonic-xcvrd/tests/test_xcvrd.py b/sonic-xcvrd/tests/test_xcvrd.py index 4eedfead5..4af09bf23 100644 --- a/sonic-xcvrd/tests/test_xcvrd.py +++ b/sonic-xcvrd/tests/test_xcvrd.py @@ -514,6 +514,19 @@ def test_CmisManagerTask_task_worker(self, mock_chassis): mock_xcvr_api.get_tx_config_power = MagicMock(return_value=0) mock_xcvr_api.get_laser_config_freq = MagicMock(return_value=0) mock_xcvr_api.get_module_type_abbreviation = MagicMock(return_value='QSFP-DD') + mock_xcvr_api.get_datapath_init_duration = MagicMock(return_value=60000.0) + mock_xcvr_api.get_datapath_deinit_duration = MagicMock(return_value=600000.0) + mock_xcvr_api.get_cmis_rev = MagicMock(return_value='5.0') + mock_xcvr_api.get_dpinit_pending = MagicMock(return_value={ + 'DPInitPending1': True, + 'DPInitPending2': True, + 'DPInitPending3': True, + 'DPInitPending4': True, + 'DPInitPending5': True, + 'DPInitPending6': True, + 'DPInitPending7': True, + 'DPInitPending8': True + }) mock_xcvr_api.get_application_advertisement = MagicMock(return_value={ 1: { 'host_electrical_interface_id': '400GAUI-8 C2M (Annex 120E)', diff --git a/sonic-xcvrd/xcvrd/xcvrd.py b/sonic-xcvrd/xcvrd/xcvrd.py index 55d5da8f1..9c536ea93 100644 --- a/sonic-xcvrd/xcvrd/xcvrd.py +++ b/sonic-xcvrd/xcvrd/xcvrd.py @@ -1073,6 +1073,12 @@ def get_cmis_application_desired(self, api, channel, speed): return (appl_code & 0xf) + def get_cmis_dp_init_duration_secs(self, api): + return api.get_datapath_init_duration()/1000 + + def get_cmis_dp_deinit_duration_secs(self, api): + return api.get_datapath_deinit_duration()/1000 + def is_cmis_application_update_required(self, api, channel, speed): """ Check if the CMIS application update is required @@ -1176,6 +1182,32 @@ def check_config_error(self, api, channel, states): return done + def check_datapath_init_pending(self, api, channel): + """ + Check if the CMIS datapath init is pending + + Args: + api: + XcvrApi object + channel: + Integer, a bitmask of the lanes on the host side + e.g. 0x5 for lane 0 and lane 2. + + Returns: + Boolean, true if all lanes are pending datapath init, otherwise false + """ + pending = True + dpinit_pending_dict = api.get_dpinit_pending() + for lane in range(self.CMIS_NUM_CHANNELS): + if ((1 << lane) & channel) == 0: + continue + key = "DPInitPending{}".format(lane + 1) + if not dpinit_pending_dict[key]: + pending = False + break + + return pending + def check_datapath_state(self, api, channel, states): """ Check if the CMIS datapath states are in the specified state @@ -1458,7 +1490,9 @@ def task_worker(self): # TODO: Make sure this doesn't impact other datapaths api.set_lpmode(False) self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_AP_CONF - self.port_dict[lport]['cmis_expired'] = now + datetime.timedelta(seconds=self.CMIS_DEF_EXPIRED) + dpDeinitDuration = self.get_cmis_dp_deinit_duration_secs(api) + self.log_notice("{} DpDeinit duration {} secs".format(lport, dpDeinitDuration)) + self.port_dict[lport]['cmis_expired'] = now + datetime.timedelta(seconds=dpDeinitDuration) elif state == self.CMIS_STATE_AP_CONF: # TODO: Use fine grained time when the CMIS memory map is available if not self.check_module_state(api, ['ModuleReady']): @@ -1494,8 +1528,14 @@ def task_worker(self): self.force_cmis_reinit(lport, retries + 1) continue - # TODO: Use fine grained time when the CMIS memory map is available - self.port_dict[lport]['cmis_expired'] = now + datetime.timedelta(seconds=self.CMIS_DEF_EXPIRED) + if getattr(api, 'get_cmis_rev', None): + # Check datapath init pending on module that supports CMIS 5.x + majorRev = int(api.get_cmis_rev().split('.')[0]) + if majorRev >= 5 and not self.check_datapath_init_pending(api, host_lanes): + self.log_notice("{}: datapath init not pending".format(lport)) + self.force_cmis_reinit(lport, retries + 1) + continue + self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_DP_INIT elif state == self.CMIS_STATE_DP_INIT: if not self.check_config_error(api, host_lanes, ['ConfigSuccess']): @@ -1515,8 +1555,9 @@ def task_worker(self): # D.1.3 Software Configuration and Initialization api.set_datapath_init(host_lanes) - # TODO: Use fine grained timeout when the CMIS memory map is available - self.port_dict[lport]['cmis_expired'] = now + datetime.timedelta(seconds=self.CMIS_DEF_EXPIRED) + dpInitDuration = self.get_cmis_dp_init_duration_secs(api) + self.log_notice("{} DpInit duration {} secs".format(lport, dpInitDuration)) + self.port_dict[lport]['cmis_expired'] = now + datetime.timedelta(seconds=dpInitDuration) self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_DP_TXON elif state == self.CMIS_STATE_DP_TXON: if not self.check_datapath_state(api, host_lanes, ['DataPathInitialized']):