Skip to content

Commit 93a65f0

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Record SRIOV PF MAC in the binding profile"
2 parents d869163 + cd03bbc commit 93a65f0

File tree

14 files changed

+807
-102
lines changed

14 files changed

+807
-102
lines changed

nova/compute/manager.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10929,6 +10929,9 @@ def _update_migrate_vifs_profile_with_pci(self,
1092910929
if profile.get('vf_num'):
1093010930
profile['vf_num'] = pci_dev.sriov_cap['vf_num']
1093110931

10932+
if pci_dev.mac_address:
10933+
profile['device_mac_address'] = pci_dev.mac_address
10934+
1093210935
mig_vif.profile = profile
1093310936
LOG.debug("Updating migrate VIF profile for port %(port_id)s:"
1093410937
"%(profile)s", {'port_id': port_id,

nova/network/neutron.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,8 @@ def _unbind_ports(self, context, ports,
684684
for profile_key in ('pci_vendor_info', 'pci_slot',
685685
constants.ALLOCATION, 'arq_uuid',
686686
'physical_network', 'card_serial_number',
687-
'vf_num', 'pf_mac_address'):
687+
'vf_num', 'pf_mac_address',
688+
'device_mac_address'):
688689
if profile_key in port_profile:
689690
del port_profile[profile_key]
690691
port_req_body['port'][constants.BINDING_PROFILE] = port_profile
@@ -1307,6 +1308,10 @@ def _update_ports_for_instance(self, context, instance, neutron,
13071308
network=network, neutron=neutron,
13081309
bind_host_id=bind_host_id,
13091310
port_arq=port_arq)
1311+
# NOTE(gibi): Remove this once we are sure that the fix for
1312+
# bug 1942329 is always present in the deployed neutron. The
1313+
# _populate_neutron_extension_values() call above already
1314+
# populated this MAC to the binding profile instead.
13101315
self._populate_pci_mac_address(instance,
13111316
request.pci_request_id, port_req_body)
13121317

@@ -1598,6 +1603,18 @@ def _get_pci_device_profile(self, pci_dev):
15981603
if pci_dev.dev_type == obj_fields.PciDeviceType.SRIOV_VF:
15991604
dev_profile.update(
16001605
self._get_vf_pci_device_profile(pci_dev))
1606+
1607+
if pci_dev.dev_type == obj_fields.PciDeviceType.SRIOV_PF:
1608+
# In general the MAC address information flows fom the neutron
1609+
# port to the device in the backend. Except for direct-physical
1610+
# ports. In that case the MAC address flows from the physical
1611+
# device, the PF, to the neutron port. So when such a port is
1612+
# being bound to a host the port's MAC address needs to be
1613+
# updated. Nova needs to put the new MAC into the binding
1614+
# profile.
1615+
if pci_dev.mac_address:
1616+
dev_profile['device_mac_address'] = pci_dev.mac_address
1617+
16011618
return dev_profile
16021619

16031620
raise exception.PciDeviceNotFound(node_id=pci_dev.compute_node_id,
@@ -3639,11 +3656,10 @@ def _get_pci_mapping_for_migration(self, instance, migration):
36393656
revert = migration and migration.status == 'reverted'
36403657
return instance.migration_context.get_pci_mapping_for_migration(revert)
36413658

3642-
def _get_port_pci_dev(self, context, instance, port):
3659+
def _get_port_pci_dev(self, instance, port):
36433660
"""Find the PCI device corresponding to the port.
36443661
Assumes the port is an SRIOV one.
36453662
3646-
:param context: The request context.
36473663
:param instance: The instance to which the port is attached.
36483664
:param port: The Neutron port, as obtained from the Neutron API
36493665
JSON form.
@@ -3731,15 +3747,14 @@ def _update_port_binding_for_instance(
37313747
raise exception.PortUpdateFailed(port_id=p['id'],
37323748
reason=_("Unable to correlate PCI slot %s") %
37333749
pci_slot)
3734-
# NOTE(artom) If migration is None, this is an unshevle, and we
3735-
# need to figure out the pci_slot from the InstancePCIRequest
3736-
# and PciDevice objects.
3750+
# NOTE(artom) If migration is None, this is an unshelve, and we
3751+
# need to figure out the pci related binding information from
3752+
# the InstancePCIRequest and PciDevice objects.
37373753
else:
3738-
pci_dev = self._get_port_pci_dev(context, instance, p)
3754+
pci_dev = self._get_port_pci_dev(instance, p)
37393755
if pci_dev:
37403756
binding_profile.update(
3741-
self._get_pci_device_profile(pci_dev)
3742-
)
3757+
self._get_pci_device_profile(pci_dev))
37433758
updates[constants.BINDING_PROFILE] = binding_profile
37443759

37453760
# NOTE(gibi): during live migration the conductor already sets the

nova/objects/pci_device.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,12 @@ def obj_make_compatible(self, primitive, target_version):
148148
reason='dev_type=%s not supported in version %s' % (
149149
dev_type, target_version))
150150

151+
def __repr__(self):
152+
return (
153+
f'PciDevice(address={self.address}, '
154+
f'compute_node_id={self.compute_node_id})'
155+
)
156+
151157
def update_device(self, dev_dict):
152158
"""Sync the content from device dictionary to device object.
153159
@@ -175,6 +181,9 @@ def update_device(self, dev_dict):
175181
# NOTE(ralonsoh): list of parameters currently added to
176182
# "extra_info" dict:
177183
# - "capabilities": dict of (strings/list of strings)
184+
# - "parent_ifname": the netdev name of the parent (PF)
185+
# device of a VF
186+
# - "mac_address": the MAC address of the PF
178187
extra_info = self.extra_info
179188
data = v if isinstance(v, str) else jsonutils.dumps(v)
180189
extra_info.update({k: data})
@@ -572,6 +581,13 @@ def sriov_cap(self):
572581
caps = jsonutils.loads(caps_json)
573582
return caps.get('sriov', {})
574583

584+
@property
585+
def mac_address(self):
586+
"""The MAC address of the PF physical device or None if the device is
587+
not a PF or if the MAC is not available.
588+
"""
589+
return self.extra_info.get('mac_address')
590+
575591

576592
@base.NovaObjectRegistry.register
577593
class PciDeviceList(base.ObjectListBase, base.NovaObject):
@@ -611,3 +627,6 @@ def get_by_parent_address(cls, context, node_id, parent_addr):
611627
parent_addr)
612628
return base.obj_make_list(context, cls(context), objects.PciDevice,
613629
db_dev_list)
630+
631+
def __repr__(self):
632+
return f"PciDeviceList(objects={[repr(obj) for obj in self.objects]})"

nova/tests/fixtures/libvirt.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ def __init__(
309309
self, dev_type, bus, slot, function, iommu_group, numa_node, *,
310310
vf_ratio=None, multiple_gpu_types=False, generic_types=False,
311311
parent=None, vend_id=None, vend_name=None, prod_id=None,
312-
prod_name=None, driver_name=None, vpd_fields=None
312+
prod_name=None, driver_name=None, vpd_fields=None, mac_address=None,
313313
):
314314
"""Populate pci devices
315315
@@ -331,6 +331,8 @@ def __init__(
331331
:param prod_id: (str) The product ID.
332332
:param prod_name: (str) The product name.
333333
:param driver_name: (str) The driver name.
334+
:param mac_address: (str) The MAC of the device.
335+
Used in case of SRIOV PFs
334336
"""
335337

336338
self.dev_type = dev_type
@@ -349,6 +351,7 @@ def __init__(
349351
self.prod_id = prod_id
350352
self.prod_name = prod_name
351353
self.driver_name = driver_name
354+
self.mac_address = mac_address
352355

353356
self.vpd_fields = vpd_fields
354357

@@ -364,7 +367,9 @@ def generate_xml(self, skip_capability=False):
364367
assert not self.vf_ratio, 'vf_ratio does not apply for PCI devices'
365368

366369
if self.dev_type in ('PF', 'VF'):
367-
assert self.vf_ratio, 'require vf_ratio for PFs and VFs'
370+
assert (
371+
self.vf_ratio is not None
372+
), 'require vf_ratio for PFs and VFs'
368373

369374
if self.dev_type == 'VF':
370375
assert self.parent, 'require parent for VFs'
@@ -497,6 +502,10 @@ def format_vpd_cap(self):
497502
def XMLDesc(self, flags):
498503
return self.pci_device
499504

505+
@property
506+
def address(self):
507+
return "0000:%02x:%02x.%1x" % (self.bus, self.slot, self.function)
508+
500509

501510
# TODO(stephenfin): Remove all of these HostFooDevicesInfo objects in favour of
502511
# a unified devices object
@@ -609,7 +618,7 @@ def add_device(
609618
self, dev_type, bus, slot, function, iommu_group, numa_node,
610619
vf_ratio=None, multiple_gpu_types=False, generic_types=False,
611620
parent=None, vend_id=None, vend_name=None, prod_id=None,
612-
prod_name=None, driver_name=None, vpd_fields=None,
621+
prod_name=None, driver_name=None, vpd_fields=None, mac_address=None,
613622
):
614623
pci_dev_name = _get_libvirt_nodedev_name(bus, slot, function)
615624

@@ -632,6 +641,7 @@ def add_device(
632641
prod_name=prod_name,
633642
driver_name=driver_name,
634643
vpd_fields=vpd_fields,
644+
mac_address=mac_address,
635645
)
636646
self.devices[pci_dev_name] = dev
637647
return dev
@@ -651,6 +661,13 @@ def get_all_mdev_capable_devices(self):
651661
return [dev for dev in self.devices
652662
if self.devices[dev].is_capable_of_mdevs]
653663

664+
def get_pci_address_mac_mapping(self):
665+
return {
666+
device.address: device.mac_address
667+
for dev_addr, device in self.devices.items()
668+
if device.mac_address
669+
}
670+
654671

655672
class FakeMdevDevice(object):
656673
template = """
@@ -2182,6 +2199,15 @@ class LibvirtFixture(fixtures.Fixture):
21822199

21832200
def __init__(self, stub_os_vif=True):
21842201
self.stub_os_vif = stub_os_vif
2202+
self.pci_address_to_mac_map = collections.defaultdict(
2203+
lambda: '52:54:00:1e:59:c6')
2204+
2205+
def update_sriov_mac_address_mapping(self, pci_address_to_mac_map):
2206+
self.pci_address_to_mac_map.update(pci_address_to_mac_map)
2207+
2208+
def fake_get_mac_by_pci_address(self, pci_addr, pf_interface=False):
2209+
res = self.pci_address_to_mac_map[pci_addr]
2210+
return res
21852211

21862212
def setUp(self):
21872213
super().setUp()
@@ -2205,7 +2231,7 @@ def setUp(self):
22052231

22062232
self.useFixture(fixtures.MockPatch(
22072233
'nova.pci.utils.get_mac_by_pci_address',
2208-
return_value='52:54:00:1e:59:c6'))
2234+
new=self.fake_get_mac_by_pci_address))
22092235

22102236
# libvirt calls out to sysfs to get the vfs ID during macvtap plug
22112237
self.useFixture(fixtures.MockPatch(

nova/tests/functional/libvirt/base.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def setUp(self):
4242
super(ServersTestBase, self).setUp()
4343

4444
self.useFixture(nova_fixtures.LibvirtImageBackendFixture())
45-
self.useFixture(nova_fixtures.LibvirtFixture())
45+
self.libvirt = self.useFixture(nova_fixtures.LibvirtFixture())
4646
self.useFixture(nova_fixtures.OSBrickFixture())
4747

4848
self.useFixture(fixtures.MockPatch(
@@ -134,6 +134,12 @@ def _start_compute(hostname, host_info):
134134
host_info, pci_info, mdev_info, vdpa_info, libvirt_version,
135135
qemu_version, hostname,
136136
)
137+
# If the compute is configured with PCI devices then we need to
138+
# make sure that the stubs around sysfs has the MAC address
139+
# information for the PCI PF devices
140+
if pci_info:
141+
self.libvirt.update_sriov_mac_address_mapping(
142+
pci_info.get_pci_address_mac_mapping())
137143
# This is fun. Firstly we need to do a global'ish mock so we can
138144
# actually start the service.
139145
with mock.patch('nova.virt.libvirt.host.Host.get_connection',
@@ -392,6 +398,22 @@ class LibvirtNeutronFixture(nova_fixtures.NeutronFixture):
392398
'binding:vnic_type': 'remote-managed',
393399
}
394400

401+
network_4_port_pf = {
402+
'id': 'c6f51315-9202-416f-9e2f-eb78b3ac36d9',
403+
'network_id': network_4['id'],
404+
'status': 'ACTIVE',
405+
'mac_address': 'b5:bc:2e:e7:51:01',
406+
'fixed_ips': [
407+
{
408+
'ip_address': '192.168.4.8',
409+
'subnet_id': subnet_4['id']
410+
}
411+
],
412+
'binding:vif_details': {'vlan': 42},
413+
'binding:vif_type': 'hostdev_physical',
414+
'binding:vnic_type': 'direct-physical',
415+
}
416+
395417
def __init__(self, test):
396418
super(LibvirtNeutronFixture, self).__init__(test)
397419
self._networks = {

0 commit comments

Comments
 (0)