From 9f3a124f80c243fdb2a4a3ea2ed412bd2e27e5b4 Mon Sep 17 00:00:00 2001 From: mihirpat1 <112018033+mihirpat1@users.noreply.github.com> Date: Mon, 3 Apr 2023 20:31:25 -0700 Subject: [PATCH] Retrieve subport from CONFIG_DB to enable breakout support (#342) Retrieve subport from CONFIG_DB to enable breakout support --------- Signed-off-by: Mihir Patel --- sonic-xcvrd/tests/test_xcvrd.py | 106 +++++++++++++++ sonic-xcvrd/xcvrd/xcvrd.py | 222 +++++++++++++++++++------------- 2 files changed, 241 insertions(+), 87 deletions(-) diff --git a/sonic-xcvrd/tests/test_xcvrd.py b/sonic-xcvrd/tests/test_xcvrd.py index 6f2cdcb83..46638453c 100644 --- a/sonic-xcvrd/tests/test_xcvrd.py +++ b/sonic-xcvrd/tests/test_xcvrd.py @@ -2,6 +2,7 @@ from xcvrd.xcvrd_utilities.port_mapping import * from xcvrd.xcvrd_utilities.sfp_status_helper import * from xcvrd.xcvrd import * +import pytest import copy import os import sys @@ -653,6 +654,111 @@ def test_CmisManagerTask_task_run_stop(self, mock_chassis): cmis_manager.join() assert not cmis_manager.is_alive() + DEFAULT_DP_STATE = { + 'DP1State': 'DataPathActivated', + 'DP2State': 'DataPathActivated', + 'DP3State': 'DataPathActivated', + 'DP4State': 'DataPathActivated', + 'DP5State': 'DataPathActivated', + 'DP6State': 'DataPathActivated', + 'DP7State': 'DataPathActivated', + 'DP8State': 'DataPathActivated' + } + DEFAULT_CONFIG_STATUS = { + 'ConfigStatusLane1': 'ConfigSuccess', + 'ConfigStatusLane2': 'ConfigSuccess', + 'ConfigStatusLane3': 'ConfigSuccess', + 'ConfigStatusLane4': 'ConfigSuccess', + 'ConfigStatusLane5': 'ConfigSuccess', + 'ConfigStatusLane6': 'ConfigSuccess', + 'ConfigStatusLane7': 'ConfigSuccess', + 'ConfigStatusLane8': 'ConfigSuccess' + } + CONFIG_LANE_8_UNDEFINED = { + 'ConfigStatusLane1': 'ConfigSuccess', + 'ConfigStatusLane2': 'ConfigSuccess', + 'ConfigStatusLane3': 'ConfigSuccess', + 'ConfigStatusLane4': 'ConfigSuccess', + 'ConfigStatusLane5': 'ConfigSuccess', + 'ConfigStatusLane6': 'ConfigSuccess', + 'ConfigStatusLane7': 'ConfigSuccess', + 'ConfigStatusLane8': 'ConfigUndefined' + } + @pytest.mark.parametrize("app_new, host_lanes_mask, lane_appl_code, default_dp_state, default_config_status, expected", [ + (1, 0x0F, {0 : 1, 1 : 1, 2 : 1, 3 : 1}, DEFAULT_DP_STATE, DEFAULT_CONFIG_STATUS, False), + (1, 0x0F, {0 : 1, 1 : 1, 2 : 1, 3 : 0}, DEFAULT_DP_STATE, DEFAULT_CONFIG_STATUS, True), + (1, 0xF0, {4 : 1, 5 : 1, 6 : 1, 7 : 1}, DEFAULT_DP_STATE, DEFAULT_CONFIG_STATUS, False), + (1, 0xF0, {4 : 1, 5 : 1, 6 : 1, 7 : 1}, DEFAULT_DP_STATE, CONFIG_LANE_8_UNDEFINED, True), + (1, 0xF0, {4 : 1, 5 : 7, 6 : 1, 7 : 1}, DEFAULT_DP_STATE, DEFAULT_CONFIG_STATUS, True), + (4, 0xF0, {4 : 1, 5 : 7, 6 : 1, 7 : 1}, DEFAULT_DP_STATE, DEFAULT_CONFIG_STATUS, True), + (3, 0xC0, {7 : 3, 8 : 3}, DEFAULT_DP_STATE, DEFAULT_CONFIG_STATUS, False), + (1, 0x0F, {}, DEFAULT_DP_STATE, DEFAULT_CONFIG_STATUS, True), + (-1, 0x0F, {}, DEFAULT_DP_STATE, DEFAULT_CONFIG_STATUS, False) + ]) + def test_CmisManagerTask_is_cmis_application_update_required(self, app_new, host_lanes_mask, lane_appl_code, default_dp_state, default_config_status, expected): + + mock_xcvr_api = MagicMock() + mock_xcvr_api.is_flat_memory = MagicMock(return_value=False) + + def get_application(lane): + return lane_appl_code.get(lane, 0) + mock_xcvr_api.get_application = MagicMock(side_effect=get_application) + + mock_xcvr_api.get_datapath_state = MagicMock(return_value=default_dp_state) + mock_xcvr_api.get_config_datapath_hostlane_status = MagicMock(return_value=default_config_status) + + port_mapping = PortMapping() + stop_event = threading.Event() + task = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping, stop_event) + + assert task.is_cmis_application_update_required(mock_xcvr_api, app_new, host_lanes_mask) == expected + + @pytest.mark.parametrize("host_lane_count, speed, subport, expected", [ + (8, 400000, 0, 0xFF), + (4, 100000, 1, 0xF), + (4, 100000, 2, 0xF0), + (4, 100000, 0, 0xF), + (4, 100000, 9, 0x0), + (1, 50000, 2, 0x2), + (1, 200000, 2, 0x0) + ]) + def test_CmisManagerTask_get_cmis_host_lanes_mask(self, host_lane_count, speed, subport, expected): + appl_advert_dict = { + 1: { + 'host_electrical_interface_id': '400GAUI-8 C2M (Annex 120E)', + 'module_media_interface_id': '400GBASE-DR4 (Cl 124)', + 'media_lane_count': 4, + 'host_lane_count': 8, + 'host_lane_assignment_options': 1 + }, + 2: { + 'host_electrical_interface_id': 'CAUI-4 C2M (Annex 83E)', + 'module_media_interface_id': 'Active Cable assembly with BER < 5x10^-5', + 'media_lane_count': 4, + 'host_lane_count': 4, + 'host_lane_assignment_options': 17 + }, + 3: { + 'host_electrical_interface_id': '50GAUI-1 C2M', + 'module_media_interface_id': '50GBASE-SR', + 'media_lane_count': 1, + 'host_lane_count': 1, + 'host_lane_assignment_options': 255 + } + } + mock_xcvr_api = MagicMock() + mock_xcvr_api.get_application_advertisement = MagicMock(return_value=appl_advert_dict) + + def get_host_lane_assignment_option_side_effect(app): + return appl_advert_dict[app]['host_lane_assignment_options'] + mock_xcvr_api.get_host_lane_assignment_option = MagicMock(side_effect=get_host_lane_assignment_option_side_effect) + port_mapping = PortMapping() + stop_event = threading.Event() + task = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping, stop_event) + + appl = task.get_cmis_application_desired(mock_xcvr_api, host_lane_count, speed) + assert task.get_cmis_host_lanes_mask(mock_xcvr_api, appl, host_lane_count, subport) == expected + @patch('xcvrd.xcvrd.platform_chassis') @patch('xcvrd.xcvrd_utilities.port_mapping.subscribe_port_update_event', MagicMock(return_value=(None, None))) @patch('xcvrd.xcvrd_utilities.port_mapping.handle_port_update_event', MagicMock()) diff --git a/sonic-xcvrd/xcvrd/xcvrd.py b/sonic-xcvrd/xcvrd/xcvrd.py index 337a63235..f9a06b08b 100644 --- a/sonic-xcvrd/xcvrd/xcvrd.py +++ b/sonic-xcvrd/xcvrd/xcvrd.py @@ -944,7 +944,7 @@ class CmisManagerTask(threading.Thread): CMIS_MAX_RETRIES = 3 CMIS_DEF_EXPIRED = 60 # seconds, default expiration time CMIS_MODULE_TYPES = ['QSFP-DD', 'QSFP_DD', 'OSFP', 'QSFP+C'] - CMIS_NUM_CHANNELS = 8 + CMIS_MAX_HOST_LANES = 8 CMIS_STATE_UNKNOWN = 'UNKNOWN' CMIS_STATE_INSERTED = 'INSERTED' @@ -1023,6 +1023,8 @@ def on_port_update_event(self, port_change_event): self.port_dict[lport]['laser_freq'] = int(port_change_event.port_dict['laser_freq']) if 'tx_power' in port_change_event.port_dict: self.port_dict[lport]['tx_power'] = float(port_change_event.port_dict['tx_power']) + if 'subport' in port_change_event.port_dict: + self.port_dict[lport]['subport'] = int(port_change_event.port_dict['subport']) self.force_cmis_reinit(lport, 0) else: @@ -1058,31 +1060,24 @@ def get_interface_speed(self, ifname): speed = 1000 return speed - def get_cmis_application_desired(self, api, channel, speed): + def get_cmis_application_desired(self, api, host_lane_count, speed): """ Get the CMIS application code that matches the specified host side configurations Args: api: XcvrApi object - channel: - Integer, a bitmask of the lanes on the host side - e.g. 0x5 for lane 0 and lane 2. + host_lane_count: + Number of lanes on the host side speed: Integer, the port speed of the host interface Returns: Integer, the transceiver-specific application code """ - if speed == 0 or channel == 0: + if speed == 0 or host_lane_count == 0: return 0 - host_lane_count = 0 - for lane in range(self.CMIS_NUM_CHANNELS): - if ((1 << lane) & channel) == 0: - continue - host_lane_count += 1 - appl_code = 0 appl_dict = api.get_application_advertisement() for c in appl_dict.keys(): @@ -1102,37 +1097,76 @@ def get_cmis_dp_init_duration_secs(self, api): 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): + def get_cmis_host_lanes_mask(self, api, appl, host_lane_count, subport): + """ + Retrieves mask of active host lanes based on appl, host lane count and subport + + Args: + api: + XcvrApi object + appl: + Integer, the transceiver-specific application code + host_lane_count: + Integer, number of lanes on the host side + subport: + Integer, 1-based logical port number of the physical port after breakout + 0 means port is a non-breakout port + + Returns: + Integer, a mask of the active lanes on the host side + e.g. 0x3 for lane 0 and lane 1. + """ + host_lanes_mask = 0 + + if appl < 1 or host_lane_count <= 0 or subport < 0: + self.log_error("Invalid input to get host lane mask - appl {} host_lane_count {} " + "subport {}!".format(appl, host_lane_count, subport)) + return host_lanes_mask + + host_lane_assignment_option = api.get_host_lane_assignment_option(appl) + host_lane_start_bit = (host_lane_count * (0 if subport == 0 else subport - 1)) + if host_lane_assignment_option & (1 << host_lane_start_bit): + host_lanes_mask = ((1 << host_lane_count) - 1) << host_lane_start_bit + else: + self.log_error("Unable to find starting host lane - host_lane_assignment_option {}" + " host_lane_start_bit {} host_lane_count {} subport {} appl {}!".format( + host_lane_assignment_option, host_lane_start_bit, host_lane_count, + subport, appl)) + + return host_lanes_mask + + def is_cmis_application_update_required(self, api, app_new, host_lanes_mask): """ Check if the CMIS application update is required Args: api: XcvrApi object - channel: + app_new: + Integer, the transceiver-specific application code for the new application + host_lanes_mask: Integer, a bitmask of the lanes on the host side e.g. 0x5 for lane 0 and lane 2. - speed: - Integer, the port speed of the host interface Returns: Boolean, true if application update is required otherwise false """ - if speed == 0 or channel == 0 or api.is_flat_memory(): + if api.is_flat_memory() or app_new <= 0 or host_lanes_mask <= 0: + self.log_error("Invalid input while checking CMIS update required - is_flat_memory {}" + "app_new {} host_lanes_mask {}!".format( + api.is_flat_memory(), app_new, host_lanes_mask)) return False - app_new = self.get_cmis_application_desired(api, channel, speed) - if app_new != 1: - self.log_notice("Non-default application is not supported") - app_old = 0 - for lane in range(self.CMIS_NUM_CHANNELS): - if ((1 << lane) & channel) == 0: + for lane in range(self.CMIS_MAX_HOST_LANES): + if ((1 << lane) & host_lanes_mask) == 0: continue if app_old == 0: app_old = api.get_application(lane) elif app_old != api.get_application(lane): - self.log_notice("Not all the lanes are in the same application mode") + self.log_notice("Not all the lanes are in the same application mode " + "app_old {} current app {} lane {} host_lanes_mask {}".format( + app_old, api.get_application(lane), lane, host_lanes_mask)) self.log_notice("Forcing application update...") return True @@ -1140,8 +1174,8 @@ def is_cmis_application_update_required(self, api, channel, speed): skip = True dp_state = api.get_datapath_state() conf_state = api.get_config_datapath_hostlane_status() - for lane in range(self.CMIS_NUM_CHANNELS): - if ((1 << lane) & channel) == 0: + for lane in range(self.CMIS_MAX_HOST_LANES): + if ((1 << lane) & host_lanes_mask) == 0: continue name = "DP{}State".format(lane + 1) if dp_state[name] != 'DataPathActivated': @@ -1177,14 +1211,14 @@ def check_module_state(self, api, states): """ return api.get_module_state() in states - def check_config_error(self, api, channel, states): + def check_config_error(self, api, host_lanes_mask, states): """ Check if the CMIS configuration states are in the specified state Args: api: XcvrApi object - channel: + host_lanes_mask: Integer, a bitmask of the lanes on the host side e.g. 0x5 for lane 0 and lane 2. states: @@ -1195,8 +1229,8 @@ def check_config_error(self, api, channel, states): """ done = True cerr = api.get_config_datapath_hostlane_status() - for lane in range(self.CMIS_NUM_CHANNELS): - if ((1 << lane) & channel) == 0: + for lane in range(self.CMIS_MAX_HOST_LANES): + if ((1 << lane) & host_lanes_mask) == 0: continue key = "ConfigStatusLane{}".format(lane + 1) if cerr[key] not in states: @@ -1205,14 +1239,14 @@ def check_config_error(self, api, channel, states): return done - def check_datapath_init_pending(self, api, channel): + def check_datapath_init_pending(self, api, host_lanes_mask): """ Check if the CMIS datapath init is pending Args: api: XcvrApi object - channel: + host_lanes_mask: Integer, a bitmask of the lanes on the host side e.g. 0x5 for lane 0 and lane 2. @@ -1221,8 +1255,8 @@ def check_datapath_init_pending(self, api, channel): """ pending = True dpinit_pending_dict = api.get_dpinit_pending() - for lane in range(self.CMIS_NUM_CHANNELS): - if ((1 << lane) & channel) == 0: + for lane in range(self.CMIS_MAX_HOST_LANES): + if ((1 << lane) & host_lanes_mask) == 0: continue key = "DPInitPending{}".format(lane + 1) if not dpinit_pending_dict[key]: @@ -1231,14 +1265,14 @@ def check_datapath_init_pending(self, api, channel): return pending - def check_datapath_state(self, api, channel, states): + def check_datapath_state(self, api, host_lanes_mask, states): """ Check if the CMIS datapath states are in the specified state Args: api: XcvrApi object - channel: + host_lanes_mask: Integer, a bitmask of the lanes on the host side e.g. 0x5 for lane 0 and lane 2. states: @@ -1249,8 +1283,8 @@ def check_datapath_state(self, api, channel, states): """ done = True dpstate = api.get_datapath_state() - for lane in range(self.CMIS_NUM_CHANNELS): - if ((1 << lane) & channel) == 0: + for lane in range(self.CMIS_MAX_HOST_LANES): + if ((1 << lane) & host_lanes_mask) == 0: continue key = "DP{}State".format(lane + 1) if dpstate[key] not in states: @@ -1326,7 +1360,7 @@ def configure_laser_frequency(self, api, lport, freq, grid=75): if chan % 3 != 0: self.log_error("{} configured freq:{} GHz is NOT in 75GHz grid".format(lport, freq)) if api.get_tuning_in_progress(): - self.log_error("{} Tuning in progress, channel selection may fail!".format(lport)) + self.log_error("{} Tuning in progress, subport selection may fail!".format(lport)) return api.set_laser_freq(freq, grid) def wait_for_port_config_done(self, namespace): @@ -1379,6 +1413,9 @@ def task_worker(self): self.CMIS_STATE_FAILED, self.CMIS_STATE_READY, self.CMIS_STATE_REMOVED]: + if state != self.CMIS_STATE_READY: + self.port_dict[lport]['appl'] = 0 + self.port_dict[lport]['host_lanes_mask'] = 0 continue # Handle the case when Xcvrd was NOT running when 'host_tx_ready' or 'admin_status' @@ -1392,19 +1429,13 @@ def task_worker(self): pport = int(info.get('index', "-1")) speed = int(info.get('speed', "0")) lanes = info.get('lanes', "").strip() - if pport < 0 or speed == 0 or len(lanes) < 1: + subport = info.get('subport', 0) + if pport < 0 or speed == 0 or len(lanes) < 1 or subport < 0: continue # Desired port speed on the host side host_speed = speed - - # Convert the physical lane list into a logical lanemask - # - # TODO: Add dynamic port breakout support by checking the physical lane offset - host_lanes = 0 - phys_lanes = lanes.split(',') - for i in range(len(phys_lanes)): - host_lanes |= (1 << i) + host_lane_count = len(lanes.split(',')) # double-check the HW presence before moving forward sfp = platform_chassis.get_sfp(pport) @@ -1450,8 +1481,17 @@ def task_worker(self): now = datetime.datetime.now() expired = self.port_dict[lport].get('cmis_expired') retries = self.port_dict[lport].get('cmis_retries', 0) - self.log_notice("{}: {}G, lanemask=0x{:x}, state={}, retries={}".format( - lport, int(speed/1000), host_lanes, state, retries)) + host_lanes_mask = self.port_dict[lport].get('host_lanes_mask', 0) + appl = self.port_dict[lport].get('appl', 0) + if state != self.CMIS_STATE_INSERTED and (host_lanes_mask <= 0 or appl < 1): + self.log_error("{}: Unexpected value for host_lanes_mask {} or appl {} in " + "{} state".format(lport, host_lanes_mask, appl, state)) + self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_FAILED + continue + + self.log_notice("{}: {}G, lanemask=0x{:x}, state={}, appl {} host_lane_count {} " + "retries={}".format(lport, int(speed/1000), host_lanes_mask, + state, appl, host_lane_count, retries)) if retries > self.CMIS_MAX_RETRIES: self.log_error("{}: FAILED".format(lport)) self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_FAILED @@ -1460,11 +1500,31 @@ def task_worker(self): try: # CMIS state transitions if state == self.CMIS_STATE_INSERTED: + self.port_dict[lport]['appl'] = self.get_cmis_application_desired(api, + host_lane_count, host_speed) + if self.port_dict[lport]['appl'] < 1: + self.log_error("{}: no suitable app for the port appl {} host_lane_count {} " + "host_speed {}".format(lport, appl, host_lane_count, host_speed)) + self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_FAILED + continue + appl = self.port_dict[lport]['appl'] + self.log_notice("{}: Setting appl={}".format(lport, appl)) + + self.port_dict[lport]['host_lanes_mask'] = self.get_cmis_host_lanes_mask(api, + appl, host_lane_count, subport) + if self.port_dict[lport]['host_lanes_mask'] <= 0: + self.log_error("{}: Invalid lane mask received - host_lane_count {} subport {} " + "appl {}!".format(lport, host_lane_count, subport, appl)) + self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_FAILED + continue + host_lanes_mask = self.port_dict[lport]['host_lanes_mask'] + self.log_notice("{}: Setting lanemask=0x{:x}".format(lport, host_lanes_mask)) + if self.port_dict[lport]['host_tx_ready'] != 'true' or \ self.port_dict[lport]['admin_status'] != 'up': self.log_notice("{} Forcing Tx laser OFF".format(lport)) # Force DataPath re-init - api.tx_disable_channel(host_lanes, True) + api.tx_disable_channel(host_lanes_mask, True) self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_READY continue # Configure the target output power if ZR module @@ -1477,13 +1537,7 @@ def task_worker(self): else: self.log_notice("{} Successfully configured Tx power = {}".format(lport, tx_power)) - appl = self.get_cmis_application_desired(api, host_lanes, host_speed) - if appl < 1: - self.log_error("{}: no suitable app for the port".format(lport)) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_FAILED - continue - - need_update = self.is_cmis_application_update_required(api, host_lanes, host_speed) + need_update = self.is_cmis_application_update_required(api, appl, host_lanes_mask) # For ZR module, Datapath needes to be re-initlialized on new channel selection if api.is_coherent_module(): @@ -1502,19 +1556,19 @@ def task_worker(self): self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_DP_DEINIT elif state == self.CMIS_STATE_DP_DEINIT: # D.2.2 Software Deinitialization - api.set_datapath_deinit(host_lanes) + api.set_datapath_deinit(host_lanes_mask) # D.1.3 Software Configuration and Initialization - if not api.tx_disable_channel(host_lanes, True): - self.log_notice("{}: unable to turn off tx power".format(lport)) + if not api.tx_disable_channel(host_lanes_mask, True): + self.log_notice("{}: unable to turn off tx power with host_lanes_mask {}".format(lport, host_lanes_mask)) self.port_dict[lport]['cmis_retries'] = retries + 1 continue - # TODO: Make sure this doesn't impact other datapaths + #Sets module to high power mode and doesn't impact datapath if module is already in high power mode api.set_lpmode(False) self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_AP_CONF dpDeinitDuration = self.get_cmis_dp_deinit_duration_secs(api) - self.log_notice("{} DpDeinit duration {} secs".format(lport, dpDeinitDuration)) + 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 @@ -1524,7 +1578,7 @@ def task_worker(self): self.force_cmis_reinit(lport, retries + 1) continue - if not self.check_datapath_state(api, host_lanes, ['DataPathDeactivated']): + if not self.check_datapath_state(api, host_lanes_mask, ['DataPathDeactivated']): if (expired is not None) and (expired <= now): self.log_notice("{}: timeout for 'DataPathDeactivated state'".format(lport)) self.force_cmis_reinit(lport, retries + 1) @@ -1540,33 +1594,27 @@ def task_worker(self): self.log_notice("{} configured laser frequency {} GHz".format(lport, freq)) # D.1.3 Software Configuration and Initialization - appl = self.get_cmis_application_desired(api, host_lanes, host_speed) - if appl < 1: - self.log_error("{}: no suitable app for the port".format(lport)) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_FAILED - continue - - if not api.set_application(host_lanes, appl): + if not api.set_application(host_lanes_mask, appl): self.log_notice("{}: unable to set application".format(lport)) self.force_cmis_reinit(lport, retries + 1) continue - 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']): + if not self.check_config_error(api, host_lanes_mask, ['ConfigSuccess']): if (expired is not None) and (expired <= now): self.log_notice("{}: timeout for 'ConfigSuccess'".format(lport)) self.force_cmis_reinit(lport, retries + 1) continue + if hasattr(api, 'get_cmis_rev'): + # 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_mask): + self.log_notice("{}: datapath init not pending".format(lport)) + self.force_cmis_reinit(lport, retries + 1) + continue + # Ensure the Datapath is NOT Activated unless the host Tx siganl is good. # NOTE: Some CMIS compliant modules may have 'auto-squelch' feature where # the module won't take datapaths to Activated state if host tries to enable @@ -1577,24 +1625,24 @@ def task_worker(self): continue # D.1.3 Software Configuration and Initialization - api.set_datapath_init(host_lanes) + api.set_datapath_init(host_lanes_mask) dpInitDuration = self.get_cmis_dp_init_duration_secs(api) - self.log_notice("{} DpInit duration {} secs".format(lport, dpInitDuration)) + 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']): + if not self.check_datapath_state(api, host_lanes_mask, ['DataPathInitialized']): if (expired is not None) and (expired <= now): self.log_notice("{}: timeout for 'DataPathInitialized'".format(lport)) self.force_cmis_reinit(lport, retries + 1) continue # Turn ON the laser - api.tx_disable_channel(host_lanes, False) + api.tx_disable_channel(host_lanes_mask, False) self.log_notice("{}: Turning ON tx power".format(lport)) self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_DP_ACTIVATE elif state == self.CMIS_STATE_DP_ACTIVATE: - if not self.check_datapath_state(api, host_lanes, ['DataPathActivated']): + if not self.check_datapath_state(api, host_lanes_mask, ['DataPathActivated']): if (expired is not None) and (expired <= now): self.log_notice("{}: timeout for 'DataPathActivated'".format(lport)) self.force_cmis_reinit(lport, retries + 1) @@ -1603,8 +1651,8 @@ def task_worker(self): self.log_notice("{}: READY".format(lport)) self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_READY - except (NotImplementedError, AttributeError): - self.log_error("{}: internal errors".format(lport)) + except (NotImplementedError, AttributeError) as e: + self.log_error("{}: internal errors due to {}".format(lport, e)) self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_FAILED self.log_notice("Stopped")