Skip to content

Commit cc56367

Browse files
authored
Xcvrd changes to support 400G ZR configuration (sonic-net#270)
* Xcvrd changes to support 400G ZR configuration * Fix test failures * Improve code coverage * Addressed review comments
1 parent e3b03d4 commit cc56367

File tree

2 files changed

+117
-6
lines changed

2 files changed

+117
-6
lines changed

sonic-xcvrd/tests/test_xcvrd.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,27 @@ def test_CmisManagerTask_handle_port_change_event(self):
443443
task.on_port_update_event(port_change_event)
444444
assert len(task.port_dict) == 1
445445

446+
447+
@patch('xcvrd.xcvrd.XcvrTableHelper')
448+
def test_CmisManagerTask_get_configured_freq(self, mock_table_helper):
449+
port_mapping = PortMapping()
450+
task = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping)
451+
cfg_port_tbl = MagicMock()
452+
cfg_port_tbl.get = MagicMock(return_value=(True, (('laser_freq', 193100),)))
453+
mock_table_helper.get_cfg_port_tbl = MagicMock(return_value=cfg_port_tbl)
454+
task.xcvr_table_helper.get_cfg_port_tbl = mock_table_helper.get_cfg_port_tbl
455+
assert task.get_configured_laser_freq_from_db('Ethernet0') == 193100
456+
457+
@patch('xcvrd.xcvrd.XcvrTableHelper')
458+
def test_CmisManagerTask_get_configured_tx_power_from_db(self, mock_table_helper):
459+
port_mapping = PortMapping()
460+
task = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping)
461+
cfg_port_tbl = MagicMock()
462+
cfg_port_tbl.get = MagicMock(return_value=(True, (('tx_power', -10),)))
463+
mock_table_helper.get_cfg_port_tbl = MagicMock(return_value=cfg_port_tbl)
464+
task.xcvr_table_helper.get_cfg_port_tbl = mock_table_helper.get_cfg_port_tbl
465+
assert task.get_configured_tx_power_from_db('Ethernet0') == -10
466+
446467
@patch('xcvrd.xcvrd.platform_chassis')
447468
@patch('xcvrd.xcvrd_utilities.port_mapping.subscribe_port_update_event', MagicMock(return_value=(None, None)))
448469
@patch('xcvrd.xcvrd_utilities.port_mapping.handle_port_update_event', MagicMock())
@@ -469,6 +490,9 @@ def test_CmisManagerTask_task_worker(self, mock_chassis):
469490
mock_xcvr_api.set_lpmode = MagicMock(return_value=True)
470491
mock_xcvr_api.set_application = MagicMock(return_value=True)
471492
mock_xcvr_api.is_flat_memory = MagicMock(return_value=False)
493+
mock_xcvr_api.is_coherent_module = MagicMock(return_value=True)
494+
mock_xcvr_api.get_tx_config_power = MagicMock(return_value=0)
495+
mock_xcvr_api.get_laser_config_freq = MagicMock(return_value=0)
472496
mock_xcvr_api.get_module_type_abbreviation = MagicMock(return_value='QSFP-DD')
473497
mock_xcvr_api.get_application_advertisement = MagicMock(return_value={
474498
1: {
@@ -551,6 +575,10 @@ def test_CmisManagerTask_task_worker(self, mock_chassis):
551575

552576
task.get_host_tx_status = MagicMock(return_value='true')
553577
task.get_port_admin_status = MagicMock(return_value='up')
578+
task.get_configured_tx_power_from_db = MagicMock(return_value=-13)
579+
task.get_configured_laser_freq_from_db = MagicMock(return_value=193100)
580+
task.configure_tx_output_power = MagicMock(return_value=1)
581+
task.configure_laser_frequency = MagicMock(return_value=1)
554582

555583
# Case 1: Module Inserted --> DP_DEINIT
556584
task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True])

sonic-xcvrd/xcvrd/xcvrd.py

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,11 @@ def on_port_update_event(self, port_change_event):
999999
# We dont have better way to check if 'admin_status' is from APPL_DB or STATE_DB so this
10001000
# check is put temporarily to listen only to APPL_DB's admin_status and ignore that of STATE_DB
10011001
self.port_dict[lport]['admin_status'] = port_change_event.port_dict['admin_status']
1002+
if 'laser_freq' in port_change_event.port_dict:
1003+
self.port_dict[lport]['laser_freq'] = int(port_change_event.port_dict['laser_freq'])
1004+
if 'tx_power' in port_change_event.port_dict:
1005+
self.port_dict[lport]['tx_power'] = float(port_change_event.port_dict['tx_power'])
1006+
10021007
self.force_cmis_reinit(lport, 0)
10031008
else:
10041009
self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_REMOVED
@@ -1121,7 +1126,6 @@ def is_cmis_application_update_required(self, api, channel, speed):
11211126
skip = False
11221127
break
11231128
return (not skip)
1124-
11251129
return True
11261130

11271131
def force_cmis_reinit(self, lport, retries=0):
@@ -1203,6 +1207,32 @@ def check_datapath_state(self, api, channel, states):
12031207

12041208
return done
12051209

1210+
def get_configured_laser_freq_from_db(self, lport):
1211+
"""
1212+
Return the Tx power configured by user in CONFIG_DB's PORT table
1213+
"""
1214+
freq = 0
1215+
asic_index = self.port_mapping.get_asic_id_for_logical_port(lport)
1216+
port_tbl = self.xcvr_table_helper.get_cfg_port_tbl(asic_index)
1217+
1218+
found, port_info = port_tbl.get(lport)
1219+
if found and 'laser_freq' in dict(port_info):
1220+
freq = dict(port_info)['laser_freq']
1221+
return int(freq)
1222+
1223+
def get_configured_tx_power_from_db(self, lport):
1224+
"""
1225+
Return the Tx power configured by user in CONFIG_DB's PORT table
1226+
"""
1227+
power = 0
1228+
asic_index = self.port_mapping.get_asic_id_for_logical_port(lport)
1229+
port_tbl = self.xcvr_table_helper.get_cfg_port_tbl(asic_index)
1230+
1231+
found, port_info = port_tbl.get(lport)
1232+
if found and 'tx_power' in dict(port_info):
1233+
power = dict(port_info)['tx_power']
1234+
return float(power)
1235+
12061236
def get_host_tx_status(self, lport):
12071237
host_tx_ready = 'false'
12081238

@@ -1226,11 +1256,31 @@ def get_port_admin_status(self, lport):
12261256
admin_status = dict(port_info)['admin_status']
12271257
return admin_status
12281258

1259+
def configure_tx_output_power(self, api, lport, tx_power):
1260+
min_p, max_p = api.get_supported_power_config()
1261+
if tx_power < min_p:
1262+
self.log_error("{} configured tx power {} < minimum power {} supported".format(lport, tx_power, min_p))
1263+
if tx_power > max_p:
1264+
self.log_error("{} configured tx power {} > maximum power {} supported".format(lport, tx_power, max_p))
1265+
return api.set_tx_power(tx_power)
1266+
1267+
def configure_laser_frequency(self, api, lport, freq):
1268+
_, _, _, lowf, highf = api.get_supported_freq_config()
1269+
if freq < lowf:
1270+
self.log_error("{} configured freq:{} GHz is lower than the supported freq:{} GHz".format(lport, freq, lowf))
1271+
if freq > highf:
1272+
self.log_error("{} configured freq:{} GHz is higher than the supported freq:{} GHz".format(lport, freq, highf))
1273+
chan = int(round((freq - 193100)/25))
1274+
if chan % 3 != 0:
1275+
self.log_error("{} configured freq:{} GHz is NOT in 75GHz grid".format(lport, freq))
1276+
if api.get_tuning_in_progress():
1277+
self.log_error("{} Tuning in progress, channel selection may fail!".format(lport))
1278+
return api.set_laser_freq(freq)
1279+
12291280
def task_worker(self):
12301281
self.xcvr_table_helper = XcvrTableHelper(self.namespaces)
12311282

12321283
self.log_notice("Starting...")
1233-
print("Starting")
12341284

12351285
# APPL_DB for CONFIG updates, and STATE_DB for insertion/removal
12361286
sel, asic_context = port_mapping.subscribe_port_update_event(self.namespaces)
@@ -1309,6 +1359,12 @@ def task_worker(self):
13091359
if (type is None) or (type not in self.CMIS_MODULE_TYPES):
13101360
self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_READY
13111361
continue
1362+
1363+
if api.is_coherent_module():
1364+
if 'tx_power' not in self.port_dict[lport]:
1365+
self.port_dict[lport]['tx_power'] = self.get_configured_tx_power_from_db(lport)
1366+
if 'laser_freq' not in self.port_dict[lport]:
1367+
self.port_dict[lport]['laser_freq'] = self.get_configured_laser_freq_from_db(lport)
13121368
except AttributeError:
13131369
# Skip if these essential routines are not available
13141370
self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_READY
@@ -1339,20 +1395,38 @@ def task_worker(self):
13391395
api.tx_disable_channel(host_lanes, True)
13401396
self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_READY
13411397
continue
1398+
# Configure the target output power if ZR module
1399+
if api.is_coherent_module():
1400+
tx_power = self.port_dict[lport]['tx_power']
1401+
# Prevent configuring same tx power multiple times
1402+
if 0 != tx_power and tx_power != api.get_tx_config_power():
1403+
if 1 != self.configure_tx_output_power(api, lport, tx_power):
1404+
self.log_error("{} failed to configure Tx power = {}".format(lport, tx_power))
1405+
else:
1406+
self.log_notice("{} Successfully configured Tx power = {}".format(lport, tx_power))
13421407

13431408
appl = self.get_cmis_application_desired(api, host_lanes, host_speed)
13441409
if appl < 1:
13451410
self.log_error("{}: no suitable app for the port".format(lport))
13461411
self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_FAILED
13471412
continue
13481413

1349-
has_update = self.is_cmis_application_update_required(api, host_lanes, host_speed)
1350-
if not has_update:
1414+
need_update = self.is_cmis_application_update_required(api, host_lanes, host_speed)
1415+
1416+
# For ZR module, Datapath needes to be re-initlialized on new channel selection
1417+
if api.is_coherent_module():
1418+
freq = self.port_dict[lport]['laser_freq']
1419+
# If user requested frequency is NOT the same as configured on the module
1420+
# force datapath re-initialization
1421+
if 0 != freq and freq != api.get_laser_config_freq():
1422+
need_update = True
1423+
1424+
if not need_update:
13511425
# No application updates
13521426
self.log_notice("{}: no CMIS application update required...READY".format(lport))
13531427
self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_READY
13541428
continue
1355-
1429+
self.log_notice("{}: force Datapath reinit".format(lport))
13561430
self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_DP_DEINIT
13571431
elif state == self.CMIS_STATE_DP_DEINIT:
13581432
# D.2.2 Software Deinitialization
@@ -1382,6 +1456,15 @@ def task_worker(self):
13821456
self.force_cmis_reinit(lport, retries + 1)
13831457
continue
13841458

1459+
if api.is_coherent_module():
1460+
# For ZR module, configure the laser frequency when Datapath is in Deactivated state
1461+
freq = self.port_dict[lport]['laser_freq']
1462+
if 0 != freq:
1463+
if 1 != self.configure_laser_frequency(api, lport, freq):
1464+
self.log_error("{} failed to configure laser frequency {} GHz".format(lport, freq))
1465+
else:
1466+
self.log_notice("{} configured laser frequency {} GHz".format(lport, freq))
1467+
13851468
# D.1.3 Software Configuration and Initialization
13861469
appl = self.get_cmis_application_desired(api, host_lanes, host_speed)
13871470
if appl < 1:
@@ -2080,7 +2163,7 @@ def init(self):
20802163
if multi_asic.is_multi_asic():
20812164
# Load the namespace details first from the database_global.json file.
20822165
swsscommon.SonicDBConfig.initializeGlobalConfig()
2083-
# To prevent race condition in get_all_namespaces() we cache the namespaces before
2166+
# To prevent race condition in get_all_namespaces() we cache the namespaces before
20842167
# creating any worker threads
20852168
self.namespaces = multi_asic.get_front_end_namespaces()
20862169

0 commit comments

Comments
 (0)