Skip to content

Credo AEC 400G firmware v4.1 needs to set low power mode when change to DPDeinit state #587

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 70 additions & 2 deletions sonic-xcvrd/tests/test_xcvrd.py
Original file line number Diff line number Diff line change
Expand Up @@ -1741,6 +1741,70 @@ def test_CmisManagerTask_get_configured_tx_power_from_db(self, mock_table_helper
task.xcvr_table_helper.get_cfg_port_tbl = mock_table_helper.get_cfg_port_tbl
assert task.get_configured_tx_power_from_db('Ethernet0') == -10

@pytest.mark.parametrize("appl, host_assign, vendor, expected", [
(1, 0x1, 'Credo', True),
(2, 0x11, 'Credo', False),
(1, 0x1, 'Molex', False)
])
def test_CmisManagerTask_need_lp_mode_for_dpdeinit(self, appl, host_assign, vendor, expected):
port_mapping = PortMapping()
stop_event = threading.Event()
task = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping, stop_event)

mock_xcvr_api = MagicMock()
mock_xcvr_api.get_manufacturer = MagicMock(return_value=vendor)
mock_xcvr_api.get_host_lane_assignment_option = MagicMock(return_value=host_assign)
assert task.need_lp_mode_for_dpdeinit(mock_xcvr_api, appl) == expected

@patch('xcvrd.xcvrd.XcvrTableHelper.get_status_tbl')
@patch('xcvrd.xcvrd.platform_chassis')
@patch('xcvrd.xcvrd.is_fast_reboot_enabled', MagicMock(return_value=(False)))
@patch('xcvrd.xcvrd.PortChangeObserver', MagicMock(handle_port_update_event=MagicMock()))
@patch('xcvrd.xcvrd._wrapper_get_sfp_type', MagicMock(return_value='QSFP_DD'))
@patch('xcvrd.xcvrd.CmisManagerTask.wait_for_port_config_done', MagicMock())
@patch('xcvrd.xcvrd.is_cmis_api', MagicMock(return_value=True))
def test_CmisManagerTask_need_lp_mode_for_dpdeinit_set_power_expired(self, mock_chassis, mock_get_status_tbl):
# set low power mode expired
mock_get_status_tbl = Table("STATE_DB", TRANSCEIVER_STATUS_TABLE)
mock_xcvr_api = MagicMock()
mock_xcvr_api.is_flat_memory = MagicMock(return_value=False)
mock_xcvr_api.is_coherent_module = MagicMock(return_value=False)
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_sfp = MagicMock()
mock_sfp.get_presence = MagicMock(return_value=True)
mock_sfp.get_xcvr_api = MagicMock(return_value=mock_xcvr_api)
mock_chassis.get_all_sfps = MagicMock(return_value=[mock_sfp])
mock_chassis.get_sfp = MagicMock(return_value=mock_sfp)

port_mapping = PortMapping()
stop_event = threading.Event()
task = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping, stop_event)
task.xcvr_table_helper = XcvrTableHelper(DEFAULT_NAMESPACE)
task.xcvr_table_helper.get_status_tbl.return_value = mock_get_status_tbl

port_change_event = PortChangeEvent('PortConfigDone', -1, 0, PortChangeEvent.PORT_SET)
task.on_port_update_event(port_change_event)
assert task.isPortConfigDone
port_change_event = PortChangeEvent('Ethernet0', 1, 0, PortChangeEvent.PORT_SET,
{'speed':'400000', 'lanes':'1,2,3,4,5,6,7,8'})
task.on_port_update_event(port_change_event)
assert len(task.port_dict) == 1
task.port_mapping.logical_port_list = MagicMock()
task.get_host_tx_status = MagicMock(return_value='true')
task.get_port_admin_status = MagicMock(return_value='up')
task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True])
task.update_port_transceiver_status_table_sw_cmis_state("Ethernet0", CMIS_STATE_DP_DEINIT)
mock_xcvr_api.get_module_state = MagicMock(return_value='ModulePwrDn')
task.need_lp_mode_for_dpdeinit = MagicMock(return_value=True)
task.port_dict['Ethernet0']['host_lanes_mask'] = 0xff
task.port_dict['Ethernet0']['appl'] = 1
task.port_dict['Ethernet0']['cmis_expired'] = datetime.datetime.now() - datetime.timedelta(100)
task.task_worker()
assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_INSERTED

@patch('xcvrd.xcvrd.platform_chassis')
@patch('xcvrd.xcvrd.is_fast_reboot_enabled', MagicMock(return_value=(False)))
@patch('xcvrd.xcvrd.PortChangeObserver', MagicMock(handle_port_update_event=MagicMock()))
Expand Down Expand Up @@ -2031,6 +2095,7 @@ def test_CmisManagerTask_task_worker(self, mock_chassis, mock_get_status_tbl):
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_module_pwr_up_duration = MagicMock(return_value=70000.0)
mock_xcvr_api.get_module_pwr_down_duration = MagicMock(return_value=70000.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_supported_freq_config = MagicMock(return_value=(0xA0,0,0,191300,196100))
Expand Down Expand Up @@ -2062,7 +2127,6 @@ def test_CmisManagerTask_task_worker(self, mock_chassis, mock_get_status_tbl):
'media_lane_assignment_options': 15
}
})
mock_xcvr_api.get_module_state = MagicMock(return_value='ModuleReady')
mock_xcvr_api.get_config_datapath_hostlane_status = MagicMock(return_value={
'ConfigStatusLane1': 'ConfigSuccess',
'ConfigStatusLane2': 'ConfigSuccess',
Expand Down Expand Up @@ -2213,17 +2277,21 @@ def test_CmisManagerTask_task_worker(self, mock_chassis, mock_get_status_tbl):
# Case 1: Module Inserted --> DP_DEINIT
task.is_appl_reconfigure_required = MagicMock(return_value=True)
mock_xcvr_api.decommission_all_datapaths = MagicMock(return_value=True)
task.need_lp_mode_for_dpdeinit = MagicMock(return_value=True)
task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True])
task.task_worker()
assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_DP_DEINIT
assert mock_xcvr_api.set_lpmode.call_count == 1
mock_xcvr_api.get_module_state = MagicMock(return_value='ModuleLowPwr')
task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True])
task.task_worker()
assert mock_xcvr_api.set_datapath_deinit.call_count == 1
assert mock_xcvr_api.tx_disable_channel.call_count == 1
assert mock_xcvr_api.set_lpmode.call_count == 1
assert mock_xcvr_api.set_lpmode.call_count == 2
assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_AP_CONF

# Case 2: DP_DEINIT --> AP Configured
mock_xcvr_api.get_module_state = MagicMock(return_value='ModuleReady')
task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True])
task.task_worker()
assert mock_xcvr_api.set_application.call_count == 1
Expand Down
37 changes: 37 additions & 0 deletions sonic-xcvrd/xcvrd/xcvrd.py
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,31 @@ def wait_for_port_config_done(self, namespace):
if key in ["PortConfigDone", "PortInitDone"]:
break

def need_lp_mode_for_dpdeinit(self, api, appl):
Copy link
Collaborator

Choose a reason for hiding this comment

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

@CharlieChenEC I don't think we can keep add vendor specific checks in a code that is generic and expects all modules to follow CMIS spec

"""
Determine whether the cmis transceiver needs to enter
low power mode when the state is changed to DPDeinit.
"""
host_assign = api.get_host_lane_assignment_option(appl)
vendor = api.get_manufacturer().strip()

if vendor == "Credo" and host_assign == 0x1:
"""
Credo AEC 400G needs this operation to work correctly on
firmware v4.1. Only one application mode 400GAUI-8 is supported
on firmware v4.1. When 'host_assign' is 1, it means the
application mode 400GAUI-8 is adopted.
Firmware v5.1 supports more application modes like 200GAUI-4,
It is not required to set low power mode when entering
DPDeinit on v5.1, and it also works fine when set low power
mode is set. In conclusion, set low power mode is only
required on Credo AEC 400G when host_assign is 0x1 to
ensure it works correctly.
"""
return True
else:
return False

def task_worker(self):
self.xcvr_table_helper = XcvrTableHelper(self.namespaces)

Expand Down Expand Up @@ -1357,7 +1382,19 @@ def task_worker(self):
continue
self.log_notice("{}: force Datapath reinit".format(lport))
self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_DP_DEINIT)
if self.need_lp_mode_for_dpdeinit(api, appl):
api.set_lpmode(True)
now = datetime.datetime.now()
modulePwrDownDuration = self.get_cmis_module_power_down_duration_secs(api)
self.log_notice("{}: ModulePwrDown duration {} secs".format(lport, modulePwrDownDuration))
self.port_dict[lport]['cmis_expired'] = now + datetime.timedelta(seconds=modulePwrDownDuration)
elif state == CMIS_STATE_DP_DEINIT:
if self.need_lp_mode_for_dpdeinit(api, appl):
if not self.check_module_state(api, ['ModuleLowPwr']):
if (expired is not None) and (expired <= now):
self.log_notice("{}: timeout for 'ModuleLowPwr'".format(lport))
self.force_cmis_reinit(lport, retries + 1)
continue
# D.2.2 Software Deinitialization
api.set_datapath_deinit(host_lanes_mask)

Expand Down
Loading