Skip to content
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

[CMIS] Add lane_mask parameter to set_loopback_mode() to enable setti… #490

Merged
merged 6 commits into from
Sep 8, 2024
111 changes: 78 additions & 33 deletions sonic_platform_base/sonic_xcvr/api/public/cmis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1115,49 +1115,94 @@ def get_loopback_capability(self):
loopback_capability['media_side_output_loopback_supported'] = bool((allowed_loopback_result >> 0) & 0x1)
return loopback_capability

def set_loopback_mode(self, loopback_mode):
def set_loopback_mode(self, loopback_mode, lane_mask = 0xff):
'''
This function sets the module loopback mode.
Loopback mode has to be one of the five:
1. "none" (default)
2. "host-side-input"
3. "host-side-output"
4. "media-side-input"
5. "media-side-output"
loopback_mode: Loopback mode has to be one of the five:
1. "none"
2. "host-side-input-none"
3. "host-side-output-none",
4. "media-side-input-none"
5. "media-side-output-none"
6. "host-side-input"
7. "host-side-output"
8. "media-side-input"
9. "media-side-output"
lane_mask: A bitmask representing which lanes to apply the loopback mode to.
The default value of 0xFF indicates that the mode should be applied to all lanes.

The function will look at 13h:128 to check advertized loopback capabilities.
Return True if the provision succeeds, False if it fails
'''
Copy link
Contributor

Choose a reason for hiding this comment

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

@xinyulin As part of clearing the loopback (line 1136-1140), can we make sure that the loopback is cleared only for the lane_mask lanes rather than all lanes?
Also, should we check if the module advertises per lane loopback support and then use lane_mask accordingly. This will be helpful to clear loopback on all lanes if per lane loopback is not supported.

Copy link
Contributor Author

@xinyulin xinyulin Aug 13, 2024

Choose a reason for hiding this comment

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

  1. Sure! we'd better to reject the request if simultaneous host media loopback mode is not supported.
  2. To clear specific lanes corresponding to the logical port for the none mode, we will need to have two lane masks (host_lane_mask and media_lane_mask for host and media individually) because the host and media lane masks might not be identical (e.g., host_lane_mask = 0x3, media_lane_mask = 0x1).
  3. If per-lane loopback mode is not supported, should we reject the request or configure the entire physical port instead?
  4. Do you think it is useful to log some messages for the reject cases?

Copy link
Contributor

Choose a reason for hiding this comment

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

@xinyulin
For 2., we can expect loopback_mode to include the type of loopback to be cleared i.e. have host-side-input-none, host-side-output-none etc. In this case, the caller can pass the lane_mask accordingly.
For 3., I think we should configure entire physical port and display a message. @prgeor Can you please confirm the expected behavior in this case?
For 4., Yes, I think it is helpful to log messages in reject cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mihirpat1 Thanks for the comments. I've updated it accordingly. Let's check if Prince has any comments on the changes.

Copy link
Contributor

Choose a reason for hiding this comment

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

@xinyulin I discussed with @prgeor regarding 3. and concluded to reject the config if per lane loopback is not supported (i.e. we should not configure unrelated lanes when loopback is intended to configure specific lanes).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mihirpat1 Thanks for the confirmation. It has been updated!

loopback_capability = self.get_loopback_capability()
if loopback_capability is None:
logger.info('Failed to get loopback capabilities')
return False
if loopback_mode == 'none':
status_host_input = self.xcvr_eeprom.write(consts.HOST_INPUT_LOOPBACK, 0)
status_host_output = self.xcvr_eeprom.write(consts.HOST_OUTPUT_LOOPBACK, 0)
status_media_input = self.xcvr_eeprom.write(consts.MEDIA_INPUT_LOOPBACK, 0)
status_media_output = self.xcvr_eeprom.write(consts.MEDIA_OUTPUT_LOOPBACK, 0)
return all([status_host_input, status_host_output, status_media_input, status_media_output])
elif loopback_mode == 'host-side-input':
if loopback_capability['host_side_input_loopback_supported']:
return self.xcvr_eeprom.write(consts.HOST_INPUT_LOOPBACK, 0xff)
else:
return False
elif loopback_mode == 'host-side-output':
if loopback_capability['host_side_output_loopback_supported']:
return self.xcvr_eeprom.write(consts.HOST_OUTPUT_LOOPBACK, 0xff)
else:
return False
elif loopback_mode == 'media-side-input':
if loopback_capability['media_side_input_loopback_supported']:
return self.xcvr_eeprom.write(consts.MEDIA_INPUT_LOOPBACK, 0xff)
else:
return False
elif loopback_mode == 'media-side-output':
if loopback_capability['media_side_output_loopback_supported']:
return self.xcvr_eeprom.write(consts.MEDIA_OUTPUT_LOOPBACK, 0xff)
else:

host_input_val = self.xcvr_eeprom.read(consts.HOST_INPUT_LOOPBACK)
host_output_val = self.xcvr_eeprom.read(consts.HOST_OUTPUT_LOOPBACK)
media_input_val = self.xcvr_eeprom.read(consts.MEDIA_INPUT_LOOPBACK)
media_output_val = self.xcvr_eeprom.read(consts.MEDIA_OUTPUT_LOOPBACK)
host_input_support = loopback_capability['host_side_input_loopback_supported']
host_output_support = loopback_capability['host_side_output_loopback_supported']
media_input_support = loopback_capability['media_side_input_loopback_supported']
media_output_support = loopback_capability['media_side_output_loopback_supported']

if lane_mask != 0xff:
if any([loopback_capability['per_lane_host_loopback_supported'] is False and 'host' in loopback_mode,
loopback_capability['per_lane_media_loopback_supported'] is False and 'media' in loopback_mode]):
txt = f'Per-lane {loopback_mode} loopback is not supported, lane_mask:{lane_mask:02x}\n'
logger.error(txt)
return False

if 'none' in loopback_mode:
if loopback_mode == 'none':
status_host_input = self.xcvr_eeprom.write(consts.HOST_INPUT_LOOPBACK, 0)
status_host_output = self.xcvr_eeprom.write(consts.HOST_OUTPUT_LOOPBACK, 0)
status_media_input = self.xcvr_eeprom.write(consts.MEDIA_INPUT_LOOPBACK, 0)
status_media_output = self.xcvr_eeprom.write(consts.MEDIA_OUTPUT_LOOPBACK, 0)
return all([status_host_input, status_host_output, status_media_input, status_media_output])

if loopback_mode == 'host-side-input-none':
return self.xcvr_eeprom.write(consts.HOST_INPUT_LOOPBACK, host_input_val & ~lane_mask)

if loopback_mode == 'host-side-output-none':
return self.xcvr_eeprom.write(consts.HOST_OUTPUT_LOOPBACK, host_output_val & ~lane_mask)

if loopback_mode == 'media-side-input-none':
return self.xcvr_eeprom.write(consts.MEDIA_INPUT_LOOPBACK, media_input_val & ~lane_mask)

if loopback_mode == 'media-side-output-none':
return self.xcvr_eeprom.write(consts.MEDIA_OUTPUT_LOOPBACK, media_output_val & ~lane_mask)
else:
return False
if loopback_capability['simultaneous_host_media_loopback_supported'] is False:
if any(['host' in loopback_mode and (media_input_val or media_output_val),
'media' in loopback_mode and (host_input_val or host_output_val)]):
txt = 'Simultaneous host media loopback is not supported\n'
txt += f'host_input_val:{host_input_val:02x}, host_output_val:{host_output_val:02x}, '
txt += f'media_input_val:{media_input_val:02x}, media_output_val:{media_output_val:02x}\n'
logger.error(txt)
return False

if loopback_mode == 'host-side-input' and host_input_support:
return self.xcvr_eeprom.write(consts.HOST_INPUT_LOOPBACK, host_input_val | lane_mask)

if loopback_mode == 'host-side-output' and host_output_support:
return self.xcvr_eeprom.write(consts.HOST_OUTPUT_LOOPBACK, host_output_val | lane_mask)

if loopback_mode == 'media-side-input' and media_input_support:
return self.xcvr_eeprom.write(consts.MEDIA_INPUT_LOOPBACK, media_input_val | lane_mask)

if loopback_mode == 'media-side-output' and media_output_support:
return self.xcvr_eeprom.write(consts.MEDIA_OUTPUT_LOOPBACK, media_output_val | lane_mask)

txt = f'Failed to set {loopback_mode} loopback, lane_mask:{lane_mask:02x}\n'
txt += f'host_input_support:{host_input_support}, host_output_support:{host_output_support}, '
txt += f'media_input_support:{media_input_support}, media_output_support:{media_output_support}\n'
txt += f'host_input_val:{host_input_val:02x}, host_output_val:{host_output_val:02x}, '
txt += f'media_input_val:{media_input_val:02x}, media_output_val:{media_output_val:02x}\n'
Copy link
Collaborator

Choose a reason for hiding this comment

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

@mihirpat1 @xinyulin can we write 4 different functions
set_host_input_loopback()
set_host_ouput_loopback()
set_media_output_loopback()
set_media_input_loopback()

And use these functions to make code more readable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@prgeor Yes, that would be much clearer to read. Do you think we should keep the set_loopback_mode() function since it has been there for years, or should we remove it and replace it with the four functions you proposed?

set_host_input_loopback(lane_mask, enable)
set_host_ouput_loopback(lane_mask, enable)
set_media_output_loopback(lane_mask, enable)
set_media_input_loopback(lane_mask, enable)

Copy link
Collaborator

Choose a reason for hiding this comment

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

@xinyulin

def set_loopback_mode(self, loopback_mode, lane_mask = 0xff):
    if (loopback_mode == host-side-ouput") :
            set_host_ouput_loopback(lane_mask, enable)
    else if  (loopback_mode == host-side-input") :
            set_host_input_loopback(lane_mask, enable)
   else if (loopback_mode == media-side-input") :
           set_media_output_loopback(lane_mask, enable)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@prgeor Thank you for the suggestions. It looks more clear and readable now.

logger.error(txt)
return False

def get_vdm(self, field_option=None):
'''
Expand Down
78 changes: 56 additions & 22 deletions tests/sonic_xcvr/test_cmis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1052,45 +1052,79 @@ def test_get_loopback_capability(self, mock_response, expected):
result = self.api.get_loopback_capability()
assert result == expected

@pytest.mark.parametrize("input_param, mock_response",[
('none', {
@pytest.mark.parametrize("input_param, mock_response, expected",[
(['none',0x0f], {
'host_side_input_loopback_supported': True,
'host_side_output_loopback_supported': True,
'media_side_input_loopback_supported': True,
'media_side_output_loopback_supported': True
}),
('host-side-input', {
'media_side_output_loopback_supported': True,
'simultaneous_host_media_loopback_supported': True,
'per_lane_host_loopback_supported': True,
'per_lane_media_loopback_supported': True
}, True),
(['host-side-input', 0x0f], {
'host_side_input_loopback_supported': True,
'host_side_output_loopback_supported': True,
'media_side_input_loopback_supported': True,
'media_side_output_loopback_supported': True
}),
('host-side-output', {
'media_side_output_loopback_supported': True,
'simultaneous_host_media_loopback_supported': True,
'per_lane_host_loopback_supported': True,
'per_lane_media_loopback_supported': True
}, True),
(['host-side-output', 0x0f], {
'host_side_input_loopback_supported': True,
'host_side_output_loopback_supported': True,
'media_side_input_loopback_supported': True,
'media_side_output_loopback_supported': True
}),
('media-side-input', {
'media_side_output_loopback_supported': True,
'simultaneous_host_media_loopback_supported': False,
'per_lane_host_loopback_supported': True,
'per_lane_media_loopback_supported': True
}, False),
(['host-side-output', 0x0f], {
'host_side_input_loopback_supported': True,
'host_side_output_loopback_supported': True,
'media_side_input_loopback_supported': True,
'media_side_output_loopback_supported': True
}),
('media-side-output', {
'media_side_output_loopback_supported': True,
'simultaneous_host_media_loopback_supported': True,
'per_lane_host_loopback_supported': False,
'per_lane_media_loopback_supported': True
}, False),
(['media-side-input', 0x0f], {
'host_side_input_loopback_supported': True,
'host_side_output_loopback_supported': True,
'media_side_input_loopback_supported': True,
'media_side_output_loopback_supported': True
}),
(
'none', None
)
])
def test_set_loopback_mode(self, input_param, mock_response):
'media_side_output_loopback_supported': True,
'simultaneous_host_media_loopback_supported': True,
'per_lane_host_loopback_supported': True,
'per_lane_media_loopback_supported': True
}, True),
(['media-side-output', 0x0f], {
'host_side_input_loopback_supported': True,
'host_side_output_loopback_supported': True,
'media_side_input_loopback_supported': True,
'media_side_output_loopback_supported': True,
'simultaneous_host_media_loopback_supported': False,
'per_lane_host_loopback_supported': False,
'per_lane_media_loopback_supported': False
}, False),
(['media-side-output', 0x0f], {
'host_side_input_loopback_supported': False,
'host_side_output_loopback_supported': False,
'media_side_input_loopback_supported': False,
'media_side_output_loopback_supported': False,
'simultaneous_host_media_loopback_supported': True,
'per_lane_host_loopback_supported': True,
'per_lane_media_loopback_supported': True
}, False),
(['none', 0x0F], None, False)
])
def test_set_loopback_mode(self, input_param, mock_response, expected):
self.api.get_loopback_capability = MagicMock()
self.api.get_loopback_capability.return_value = mock_response
self.api.set_loopback_mode(input_param)
self.api.xcvr_eeprom.read = MagicMock()
self.api.xcvr_eeprom.read.side_effect = [0xf0,0,0xf0,0]
result = self.api.set_loopback_mode(input_param[0], input_param[1])
assert result == expected

@pytest.mark.parametrize("mock_response, expected",[
(
Expand Down
Loading