diff --git a/sonic-xcvrd/tests/test_xcvrd.py b/sonic-xcvrd/tests/test_xcvrd.py index 96b479f83..23f0cc8bf 100644 --- a/sonic-xcvrd/tests/test_xcvrd.py +++ b/sonic-xcvrd/tests/test_xcvrd.py @@ -515,6 +515,17 @@ def test_CmisManagerTask_task_worker(self, mock_chassis): 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_dpinit_pending = MagicMock(return_value={ + 'DPInitPending1': False, + 'DPInitPending2': False, + 'DPInitPending3': False, + 'DPInitPending4': False, + 'DPInitPending5': False, + 'DPInitPending6': False, + 'DPInitPending7': False, + 'DPInitPending8': False + }) mock_xcvr_api.get_application_advertisement = MagicMock(return_value={ 1: { 'host_electrical_interface_id': '400GAUI-8 C2M (Annex 120E)', @@ -705,7 +716,7 @@ def test_SfpStateUpdateTask_handle_port_change_event(self, mock_table_helper): port_change_event = PortChangeEvent('Ethernet0', 1, 0, PortChangeEvent.PORT_ADD) wait_time = 5 while wait_time > 0: - task.on_port_config_change(None, port_change_event) + task.on_port_config_change(port_change_event) if task.port_mapping.logical_port_list: break wait_time -= 1 @@ -718,7 +729,7 @@ def test_SfpStateUpdateTask_handle_port_change_event(self, mock_table_helper): port_change_event = PortChangeEvent('Ethernet0', 1, 0, PortChangeEvent.PORT_REMOVE) wait_time = 5 while wait_time > 0: - task.on_port_config_change(None, port_change_event) + task.on_port_config_change(port_change_event) if not task.port_mapping.logical_port_list: break wait_time -= 1 diff --git a/sonic-xcvrd/xcvrd/xcvrd.py b/sonic-xcvrd/xcvrd/xcvrd.py index 5eb04a39a..206706a37 100644 --- a/sonic-xcvrd/xcvrd/xcvrd.py +++ b/sonic-xcvrd/xcvrd/xcvrd.py @@ -1090,7 +1090,10 @@ def get_cmis_application_desired(self, api, channel, speed): return (appl_code & 0xf) def get_cmis_dp_init_duration(self, api): - return max(api.get_datapath_init_duration()/1000, self.CMIS_DEF_EXPIRED) + return api.get_datapath_init_duration()/1000 + + def get_cmis_dp_deinit_duration(self, api): + return api.get_datapath_deinit_duration()/1000 def is_cmis_application_update_required(self, api, channel, speed): """ @@ -1195,6 +1198,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 any lanes are pending datapath init, otherwise false + """ + pending = False + 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 dpinit_pending_dict[key]: + pending = True + break + + return pending + def check_datapath_state(self, api, channel, states): """ Check if the CMIS datapath states are in the specified state @@ -1477,7 +1506,7 @@ 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.get_cmis_dp_init_duration(api)) + self.port_dict[lport]['cmis_expired'] = now + datetime.timedelta(seconds=self.get_cmis_dp_deinit_duration(api)) 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']): @@ -1513,8 +1542,11 @@ 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.get_cmis_dp_init_duration(api)) + if self.check_datapath_init_pending(api, host_lanes): + self.log_notice("{}: datapath init 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']): @@ -1534,7 +1566,6 @@ 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.get_cmis_dp_init_duration(api)) self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_DP_TXON elif state == self.CMIS_STATE_DP_TXON: @@ -1956,7 +1987,7 @@ def task_stop(self): self.task_stopping_event.set() os.kill(self.task_process.pid, signal.SIGKILL) - def on_port_config_change(self, stopping_event, port_change_event): + def on_port_config_change(self , port_change_event): if port_change_event.event_type == port_mapping.PortChangeEvent.PORT_REMOVE: self.on_remove_logical_port(port_change_event) self.port_mapping.handle_port_change_event(port_change_event)