Handle N3000 FPGA reset

A recent commit (6d639fca5d) was introduced that resets any Intel
N3000 FPGA devices on startup.  After an FPGA reset, it is possible
that the PCI addresses of the underlying FEC device may change, in
addition to the resetting of any configured drivers and SR-IOV VF
configuration on the FPGA FEC and NIC devices.

To handle the reset, it is required to do the following:

- Do not allow a user to configure an FEC device before the first
unlock (to allow the reset to occur).

- Delay the configuration of FEC and NIC devices until the reset
  has occurred.

Story: 2006740
Task: 39947

Change-Id: I6ef8225cce5bf2d4f0ded2e20b6375504a05e587
Signed-off-by: Steven Webster <steven.webster@windriver.com>
This commit is contained in:
Steven Webster 2020-06-22 09:53:00 -04:00
parent cb7b4757e3
commit 0e4d84069b
7 changed files with 77 additions and 5 deletions

View File

@ -354,6 +354,11 @@ def _check_field(field):
def _check_device_sriov(device, host):
sriov_update = False
if (device['pdevice_id'] == dconstants.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF and
host.invprovision != constants.PROVISIONED):
raise wsme.exc.ClientSideError(_("Cannot configure device %s "
"until host %s is unlocked for the first time." %
(device['uuid'], host.hostname)))
if (device['pdevice_id'] not in dconstants.SRIOV_ENABLED_DEVICE_IDS and
'sriov_numvfs' in device.keys() and device['sriov_numvfs']):

View File

@ -8,6 +8,7 @@
import collections
import copy
import re
from oslo_log import log
from sysinv.common import constants
@ -124,6 +125,21 @@ def get_sriov_interface_port(context, iface):
return get_interface_port(context, iface)
def get_sriov_interface_device_id(context, iface):
"""
Determine the underlying PCI device id of the SR-IOV interface.
"""
# The device id can be found by inspecting the '[xxxx]' at the
# end of the port's pdevice field
device_id = None
port = get_sriov_interface_port(context, iface)
if port:
device_id = re.search(r'\[([0-9a-fA-F]{1,4})\]$', port['pdevice'])
if device_id:
device_id = device_id.group(1)
return device_id
def get_sriov_interface_vf_addrs(context, iface, vf_addr_list):
"""
Determine the virtual function addresses of SR-IOV interface,

View File

@ -66,6 +66,9 @@ class DevicePuppet(base.BasePuppet):
device_config = {}
vf_config = {}
for device in fpga_fec_devices:
if not device.get('driver', None) or not device.get('sriov_numvfs', None):
continue
name = 'pci-%s' % device.pciaddr
# Format the vf addresses as quoted strings in order to prevent
@ -87,7 +90,8 @@ class DevicePuppet(base.BasePuppet):
device.pciaddr: {
'num_vfs': device['sriov_numvfs'],
'addr': quoted_str(device['pciaddr'].strip()),
'driver': device['driver']
'driver': device['driver'],
'device_id': device['pdevice_id']
}
}
device_config = {

View File

@ -1009,6 +1009,13 @@ def get_sriov_interface_port(context, iface):
return interface.get_sriov_interface_port(context, iface)
def get_sriov_interface_device_id(context, iface):
"""
Determine the underlying PCI device id of the SR-IOV interface.
"""
return interface.get_sriov_interface_device_id(context, iface)
def get_sriov_interface_vf_addrs(context, iface, vf_addr_list):
"""
Determine the virtual function addresses of SR-IOV interface,
@ -1072,6 +1079,8 @@ def get_sriov_config(context, iface):
'ifname': iface['ifname'],
'addr': quoted_str(port['pciaddr'].strip()),
'num_vfs': num_vfs,
'device_id': interface.get_sriov_interface_device_id(context, iface),
'port_name': port['name'],
'vf_config': vf_config
}
return config

View File

@ -57,7 +57,7 @@ class TestDevice(base.FunctionalTest, dbbase.BaseHostTestCase):
self.controller = host
else:
self.worker = host
return
return host
def _create_device(self, **kw):
device = dbutils.create_test_pci_device(**kw)
@ -379,3 +379,23 @@ class TestPatchDevice(TestDevice):
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
self.assertIn('attribute restricted', response.json['error_message'])
def test_device_modify_fec_device_unprovisioned(self):
host = self._create_host(constants.CONTROLLER,
subfunction=constants.WORKER,
admin=constants.ADMIN_LOCKED,
invprovision="provisioning")
self.pci_device = dbutils.create_test_pci_device(
host_id=host.id,
pclass_id=dconstants.PCI_DEVICE_CLASS_FPGA,
pdevice_id=dconstants.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF,
sriov_totalvfs=8,
sriov_numvfs=2)
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
sriov_numvfs=-1,
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
self.assertIn('is unlocked for the first time', response.json['error_message'])

View File

@ -961,6 +961,7 @@ def get_test_ethernet_port(**kw):
'interface_id': kw.get('interface_id'),
'interface_uuid': kw.get('interface_uuid'),
'pciaddr': kw.get('pciaddr'),
'pdevice': kw.get('pdevice'),
'dpdksupport': kw.get('dpdksupport'),
'dev_id': kw.get('dev_id'),
'sriov_totalvfs': kw.get('sriov_totalvfs'),

View File

@ -166,6 +166,7 @@ class InterfaceTestCaseMixin(base.PuppetTestCaseMixin):
'mac': interface['imac'],
'driver': kwargs.get('driver', 'ixgbe'),
'dpdksupport': kwargs.get('dpdksupport', True),
'pdevice': "Ethernet Controller X710 for 10GbE SFP+ [1572]",
'pciaddr': kwargs.get('pciaddr',
'0000:00:00.' + str(port_id + 1)),
'dev_id': kwargs.get('dev_id', 0),
@ -787,6 +788,15 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
self.context, self.iface)
self.assertIsNone(classifier)
def test_get_sriov_interface_device_id(self):
port, iface = self._create_ethernet_test(
'sriov1', constants.INTERFACE_CLASS_PCI_SRIOV,
constants.NETWORK_TYPE_PCI_SRIOV, sriov_numvfs=2,
sriov_vf_driver=None)
self._update_context()
value = interface.get_sriov_interface_device_id(self.context, iface)
self.assertEqual(value, '1572')
def test_get_sriov_interface_port(self):
port, iface = self._create_ethernet_test(
'sriov1', constants.INTERFACE_CLASS_PCI_SRIOV,
@ -1114,12 +1124,14 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
def _get_sriov_config(self, ifname='default',
vf_driver=constants.SRIOV_DRIVER_TYPE_VFIO,
vf_addrs=None, num_vfs=2,
pf_addr=None):
pf_addr=None, device_id='1572', port_name="eth0"):
if vf_addrs is None:
vf_addrs = []
config = {'ifname': ifname,
'addr': pf_addr if pf_addr else self.port['pciaddr'],
'device_id': device_id,
'num_vfs': num_vfs,
'port_name': port_name,
'vf_config': {}}
if vf_addrs:
for addr in vf_addrs:
@ -1434,6 +1446,8 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
def test_get_sriov_config_netdevice(self):
vf_addr1 = "0000:81:00.0"
vf_addr2 = "0000:81:01.0"
device_id = '1572'
port_name = 'eth0'
vf_addr_list = "{},{}".format(vf_addr1, vf_addr2)
num_vfs = 2
@ -1444,7 +1458,9 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
self.iface['ifname'], 'i40evf',
[quoted_str(vf_addr1),
quoted_str(vf_addr2)],
num_vfs)
num_vfs,
device_id=device_id,
port_name=port_name)
self.assertEqual(expected, config)
def test_get_sriov_config_vfio(self):
@ -1490,7 +1506,8 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
expected = self._get_sriov_config(
vf['ifname'], None,
None,
None, pf_addr=port['pciaddr'])
None, pf_addr=port['pciaddr'],
port_name="eth1")
self.assertEqual(expected, config)
def test_is_a_mellanox_cx3_device_false(self):