From 765e98a1170471c6b217fb6aa9a20420d24af6b0 Mon Sep 17 00:00:00 2001 From: nappoo Date: Wed, 22 Jun 2022 10:42:00 +0800 Subject: [PATCH] Add nova patch for smartNIC Signed-off-by: nappoo --- nova_smartNIC.patch | 595 ++++++++++++++++++++++++++++++++++++++++++++ openstack-nova.spec | 6 +- 2 files changed, 600 insertions(+), 1 deletion(-) create mode 100644 nova_smartNIC.patch diff --git a/nova_smartNIC.patch b/nova_smartNIC.patch new file mode 100644 index 0000000..6131e29 --- /dev/null +++ b/nova_smartNIC.patch @@ -0,0 +1,595 @@ +diff --git a/tools/nova/nova-20.6.1/nova/compute/manager.py b/tools/nova/nova-20.6.1/nova/compute/manager.py +index 6e5d72f..f2afe21 100644 +--- a/nova/compute/manager.py ++++ b/nova/compute/manager.py +@@ -6611,6 +6611,11 @@ class ComputeManager(manager.Manager): + # cinder v3 api flow + self.volume_api.attachment_delete(context, bdm.attachment_id) + ++ def _unclaim_and_remove_pci_devices(self, context, instance, pci_device): ++ if pci_device: ++ self.rt.unclaim_pci_devices(context, pci_device, instance) ++ instance.remove_pci_device_and_request(pci_device) ++ + def _deallocate_port_for_instance(self, context, instance, port_id, + raise_on_failure=False): + try: +@@ -6645,6 +6650,42 @@ class ComputeManager(manager.Manager): + {'port_id': port_id, 'error': ex}, + instance=instance) + ++ def _claim_pci_device_for_interface_attach( ++ self, ++ context: nova.context.RequestContext, ++ instance: 'objects.Instance', ++ pci_reqs: 'objects.InstancePCIRequests', ++ ): ++ """Claim PCI devices if there are PCI requests ++ ++ :param context: nova.context.RequestContext ++ :param instance: the objects.Instance to where the interface is being ++ attached ++ :param pci_reqs: A InstancePCIRequests object describing the ++ needed PCI devices ++ :raises InterfaceAttachPciClaimFailed: if the PCI device claim fails ++ :returns: An objects.PciDevice describing the claimed PCI device for ++ the interface or None if no device is requested ++ """ ++ ++ if not pci_reqs.requests: ++ return None ++ ++ devices = self.rt.claim_pci_devices(context, pci_reqs) ++ ++ if not devices: ++ LOG.info('Failed to claim PCI devices during interface attach ' ++ 'for PCI request %s', pci_reqs, instance=instance) ++ raise exception.InterfaceAttachPciClaimFailed( ++ instance_uuid=instance.uuid) ++ ++ # NOTE(gibi): We assume that maximum one PCI devices is attached per ++ # interface attach request. ++ device = devices[0] ++ instance.pci_devices.objects.append(device) ++ ++ return device ++ + # TODO(mriedem): There are likely race failures which can result in + # NotFound and QuotaError exceptions getting traced as well. + @messaging.expected_exceptions( +@@ -6680,9 +6721,50 @@ class ComputeManager(manager.Manager): + phase=fields.NotificationPhase.START) + + bind_host_id = self.driver.network_binding_host_id(context, instance) +- network_info = self.network_api.allocate_port_for_instance( +- context, instance, port_id, network_id, requested_ip, +- bind_host_id=bind_host_id, tag=tag) ++ ++ requested_networks = objects.NetworkRequestList( ++ objects=[ ++ objects.NetworkRequest( ++ network_id=network_id, ++ port_id=port_id, ++ address=requested_ip, ++ tag=tag, ++ ) ++ ] ++ ) ++ ++ if len(requested_networks) != 1: ++ LOG.warning( ++ "Interface attach only supports one interface per attach " ++ "request", instance=instance) ++ raise exception.InterfaceAttachFailed(instance_uuid=instance.uuid) ++ ++ pci_reqs = objects.InstancePCIRequests( ++ requests=[], instance_uuid=instance.uuid) ++ ++ self.network_api.create_resource_requests( ++ context, ++ requested_networks, ++ pci_reqs) ++ ++ try: ++ pci_device = self._claim_pci_device_for_interface_attach( ++ context, instance, pci_reqs) ++ except exception.InterfaceAttachPciClaimFailed: ++ raise exception.InterfaceAttachFailed( ++ instance_uuid=instance.uuid) ++ ++ instance.pci_requests.requests.extend(pci_reqs.requests) ++ ++ network_info = self.network_api.allocate_for_instance( ++ context, ++ instance, ++ False, ++ requested_networks, ++ bind_host_id=bind_host_id, ++ attach=True, ++ ) ++ + if len(network_info) != 1: + LOG.error('allocate_port_for_instance returned %(ports)s ' + 'ports', {'ports': len(network_info)}) +@@ -6702,6 +6784,7 @@ class ComputeManager(manager.Manager): + {'port_id': port_id, 'msg': ex}, + instance=instance) + self._deallocate_port_for_instance(context, instance, port_id) ++ self._unclaim_and_remove_pci_devices(context, instance, pci_device) + + tb = traceback.format_exc() + compute_utils.notify_about_instance_action( +@@ -6713,6 +6796,11 @@ class ComputeManager(manager.Manager): + raise exception.InterfaceAttachFailed( + instance_uuid=instance.uuid) + ++ if pci_device: ++ self.rt.allocate_pci_devices_for_instance(context, instance) ++ ++ instance.save() ++ + compute_utils.notify_about_instance_action( + context, instance, self.host, + action=fields.NotificationAction.INTERFACE_ATTACH, +@@ -6735,6 +6823,25 @@ class ComputeManager(manager.Manager): + raise exception.PortNotFound(_("Port %s is not " + "attached") % port_id) + ++ pci_req = pci_req_module.get_instance_pci_request_from_vif( ++ context, instance, condemned) ++ ++ pci_device = None ++ if pci_req: ++ pci_devices = [pci_device ++ for pci_device in instance.pci_devices.objects ++ if pci_device.request_id == pci_req.request_id] ++ ++ if not pci_devices: ++ LOG.warning( ++ "Detach interface failed, port_id=%(port_id)s, " ++ "reason: PCI device not found for PCI request %(pci_req)s", ++ {'port_id': port_id, 'pci_req': pci_req}) ++ raise exception.InterfaceDetachFailed( ++ instance_uuid=instance.uuid) ++ ++ pci_device = pci_devices[0] ++ + compute_utils.notify_about_instance_action( + context, instance, self.host, + action=fields.NotificationAction.INTERFACE_DETACH, +@@ -6756,6 +6863,9 @@ class ComputeManager(manager.Manager): + else: + self._deallocate_port_for_instance( + context, instance, port_id, raise_on_failure=True) ++ self._unclaim_and_remove_pci_devices(context, instance, pci_device) ++ ++ instance.save() + + compute_utils.notify_about_instance_action( + context, instance, self.host, +diff --git a/tools/nova/nova-20.6.1/nova/compute/resource_tracker.py b/tools/nova/nova-20.6.1/nova/compute/resource_tracker.py +index b2ba014..4106ed7 100644 +--- a/nova/compute/resource_tracker.py ++++ b/nova/compute/resource_tracker.py +@@ -1772,6 +1772,18 @@ class ResourceTracker(object): + self.pci_tracker.save(context) + return result + ++ @utils.synchronized(COMPUTE_RESOURCE_SEMAPHORE, fair=True) ++ def unclaim_pci_devices(self, context, pci_device, instance): ++ """Deallocate PCI devices ++ ++ :param context: security context ++ :param pci_device: the objects.PciDevice describing the PCI device to ++ be freed ++ :param instance: the objects.Instance the PCI resources are freed from ++ """ ++ self.pci_tracker.free_device(pci_device, instance) ++ self.pci_tracker.save(context) ++ + @utils.synchronized(COMPUTE_RESOURCE_SEMAPHORE) + def allocate_pci_devices_for_instance(self, context, instance): + """Allocate instance claimed PCI resources +diff --git a/tools/nova/nova-20.6.1/nova/exception.py b/tools/nova/nova-20.6.1/nova/exception.py +index d178034..2fd4932 100644 +--- a/nova/exception.py ++++ b/nova/exception.py +@@ -690,6 +690,11 @@ class VolumeBDMPathNotFound(VolumeBDMNotFound): + msg_fmt = _("No volume Block Device Mapping at path: %(path)s") + + ++class InterfaceAttachPciClaimFailed(Invalid): ++ msg_fmt = _("Failed to claim PCI device for %(instance_uuid)s during " ++ "interface attach") ++ ++ + class DeviceDetachFailed(NovaException): + msg_fmt = _("Device detach failed for %(device)s: %(reason)s") + +diff --git a/tools/nova/nova-20.6.1/nova/network/neutronv2/api.py b/tools/nova/nova-20.6.1/nova/network/neutronv2/api.py +index 8f85a74..c8f505d 100644 +--- a/nova/network/neutronv2/api.py ++++ b/nova/network/neutronv2/api.py +@@ -693,10 +693,15 @@ class API(base_api.NetworkAPI): + # SR-IOV port attach is not supported. + vnic_type = port.get('binding:vnic_type', + network_model.VNIC_TYPE_NORMAL) ++ profile = port.get('binding:profile', None) ++ hw_type = None ++ if profile: ++ hw_type = profile.get('hw_type', None) + if vnic_type in network_model.VNIC_TYPES_SRIOV: +- raise exception.AttachSRIOVPortNotSupported( +- port_id=port['id'], +- instance_uuid=instance.uuid) ++ if hw_type != 'direct': ++ raise exception.AttachSRIOVPortNotSupported( ++ port_id=port['id'], ++ instance_uuid=instance.uuid) + + # If requesting a specific port, automatically process + # the network for that port as if it were explicitly +diff --git a/tools/nova/nova-20.6.1/nova/network/os_vif_util.py b/tools/nova/nova-20.6.1/nova/network/os_vif_util.py +index 9b76ef3..cc5e03a 100644 +--- a/nova/network/os_vif_util.py ++++ b/nova/network/os_vif_util.py +@@ -285,6 +285,27 @@ def _set_representor_datapath_offload_settings(vif, obj): + obj.port_profile.datapath_offload = datapath_offload + + ++def _get_direct_vnic_direct_vif_instance(vif, port_profile, plugin, set_bridge=True): ++ vif_name = ('direct' + vif['id'])[:model.NIC_NAME_LEN] ++ obj = _get_vif_instance( ++ vif, ++ objects.vif.VIFDirectDevice, ++ port_profile=port_profile, ++ plugin=plugin, ++ pci_id=vif["profile"]["pci_slot"], ++ vif_name=vif_name, ++ hw_type='dpdk', ++ ) ++ if set_bridge and vif["network"]["bridge"] is not None: ++ obj.network.bridge = vif["network"]["bridge"] ++ ++ max_queues = vif['profile'].get('max_queues', None) ++ obj.max_queues = max_queues ++ n_rxq = vif['profile'].get('n_rxq', None) ++ obj.n_rxq = n_rxq ++ return obj ++ ++ + def _get_vnic_direct_vif_instance(vif, port_profile, plugin, set_bridge=True): + """Instantiate an os-vif VIF instance for ``vnic_type`` = VNIC_TYPE_DIRECT + +@@ -347,6 +368,12 @@ def _nova_to_osvif_vif_ovs(vif): + interface_id=vif.get('ovs_interfaceid') or vif['id'], + datapath_type=vif['details'].get( + model.VIF_DETAILS_OVS_DATAPATH_TYPE)) ++ ++ hw_type = vif['profile'].get('hw_type', None) ++ if hw_type == 'direct' and vnic_type == model.VNIC_TYPE_DIRECT: ++ obj = _get_direct_vnic_direct_vif_instance(vif, port_profile=profile, plugin="ovs") ++ return obj ++ + if vnic_type == model.VNIC_TYPE_DIRECT: + obj = _get_vnic_direct_vif_instance( + vif, +diff --git a/tools/nova/nova-20.6.1/nova/objects/instance.py b/tools/nova/nova-20.6.1/nova/objects/instance.py +index ba0ea15..7dbce27 100644 +--- a/nova/objects/instance.py ++++ b/nova/objects/instance.py +@@ -1207,6 +1207,14 @@ class Instance(base.NovaPersistentObject, base.NovaObject, + return objects.BlockDeviceMappingList.get_by_instance_uuid( + self._context, self.uuid) + ++ def remove_pci_device_and_request(self, pci_device): ++ """Remove the PciDevice and the related InstancePciRequest""" ++ if pci_device in self.pci_devices.objects: ++ self.pci_devices.objects.remove(pci_device) ++ self.pci_requests.requests = [ ++ pci_req for pci_req in self.pci_requests.requests ++ if pci_req.request_id != pci_device.request_id] ++ + + def _make_instance_list(context, inst_list, db_inst_list, expected_attrs): + get_fault = expected_attrs and 'fault' in expected_attrs +diff --git a/tools/nova/nova-20.6.1/nova/pci/utils.py b/tools/nova/nova-20.6.1/nova/pci/utils.py +index 5b0a082..5acdc21 100644 +--- a/nova/pci/utils.py ++++ b/nova/pci/utils.py +@@ -221,6 +221,6 @@ def get_net_name_by_vf_pci_address(vfaddress): + return ("net_%(ifname)s_%(mac)s" % + {'ifname': ifname, 'mac': '_'.join(mac)}) + except Exception: +- LOG.warning("No net device was found for VF %(vfaddress)s", +- {'vfaddress': vfaddress}) ++ #LOG.warning("No net device was found for VF %(vfaddress)s", ++ # {'vfaddress': vfaddress}) + return +diff --git a/tools/nova/nova-20.6.1/nova/virt/libvirt/config.py b/tools/nova/nova-20.6.1/nova/virt/libvirt/config.py +index 39c4da8..f2c1a1a 100644 +--- a/nova/virt/libvirt/config.py ++++ b/nova/virt/libvirt/config.py +@@ -2020,6 +2020,9 @@ class LibvirtConfigGuestHostdevPCI(LibvirtConfigGuestHostdev): + def format_dom(self): + dev = super(LibvirtConfigGuestHostdevPCI, self).format_dom() + ++ driver = etree.Element("driver", name = 'vfio') ++ dev.append(driver) ++ + address = etree.Element("address", + domain='0x' + self.domain, + bus='0x' + self.bus, +diff --git a/tools/nova/nova-20.6.1/nova/virt/libvirt/driver.py b/tools/nova/nova-20.6.1/nova/virt/libvirt/driver.py +index f51463f..1e0f566 100644 +--- a/nova/virt/libvirt/driver.py ++++ b/nova/virt/libvirt/driver.py +@@ -4145,20 +4145,31 @@ class LibvirtDriver(driver.ComputeDriver): + guest.detach_device(self._get_guest_pci_device(dev), live=True) + # after detachDeviceFlags returned, we should check the dom to + # ensure the detaching is finished +- xml = guest.get_xml_desc() +- xml_doc = etree.fromstring(xml) +- guest_config = vconfig.LibvirtConfigGuest() +- guest_config.parse_dom(xml_doc) +- +- for hdev in [d for d in guest_config.devices +- if isinstance(d, vconfig.LibvirtConfigGuestHostdevPCI)]: +- hdbsf = [hdev.domain, hdev.bus, hdev.slot, hdev.function] +- dbsf = pci_utils.parse_address(dev.address) +- if [int(x, 16) for x in hdbsf] ==\ +- [int(x, 16) for x in dbsf]: +- raise exception.PciDeviceDetachFailed(reason= +- "timeout", +- dev=dev) ++ ++ # Fixed the bug that open-source port removal does not wait. ++ count = 0 ++ err_times = 0 ++ while (count < 30): ++ xml = guest.get_xml_desc() ++ xml_doc = etree.fromstring(xml) ++ guest_config = vconfig.LibvirtConfigGuest() ++ guest_config.parse_dom(xml_doc) ++ old_err_times = copy.deepcopy(err_times) ++ ++ for hdev in [d for d in guest_config.devices ++ if isinstance(d, vconfig.LibvirtConfigGuestHostdevPCI)]: ++ hdbsf = [hdev.domain, hdev.bus, hdev.slot, hdev.function] ++ dbsf = pci_utils.parse_address(dev.address) ++ if [int(x, 16) for x in hdbsf] ==\ ++ [int(x, 16) for x in dbsf]: ++ err_times = err_times + 1 ++ if old_err_times == err_times: ++ break ++ time.sleep(1) ++ count = count + 1 ++ ++ if count == 30: ++ raise exception.PciDeviceDetachFailed(reason="timeout", dev=dev) + + except libvirt.libvirtError as ex: + error_code = ex.get_error_code() +@@ -4196,7 +4207,8 @@ class LibvirtDriver(driver.ComputeDriver): + if self._has_direct_passthrough_port(network_info): + for vif in network_info: + if (vif['vnic_type'] in +- network_model.VNIC_TYPES_DIRECT_PASSTHROUGH): ++ network_model.VNIC_TYPES_DIRECT_PASSTHROUGH and ++ vif['profile'].get('hw_type', None) != 'direct'): + cfg = self.vif_driver.get_config(instance, + vif, + instance.image_meta, +@@ -4222,7 +4234,8 @@ class LibvirtDriver(driver.ComputeDriver): + for vif in network_info + if (vif['vnic_type'] in + network_model.VNIC_TYPES_DIRECT_PASSTHROUGH and +- vif['profile'].get('pci_slot') is not None) ++ vif['profile'].get('pci_slot') is not None and ++ vif['profile'].get('hw_type', None) != 'direct') + ] + + # use detach_pci_devices to avoid failure in case of +@@ -8587,7 +8600,11 @@ class LibvirtDriver(driver.ComputeDriver): + if vif['vnic_type'] in direct_vnics: + LOG.info("Detaching vif %s from instnace " + "%s for live migration", vif['id'], instance.id) +- self.detach_interface(context, instance, vif) ++ hw_type = vif['profile'].get('hw_type', None) ++ if hw_type == 'direct': ++ LOG.warning("vif %s support live migration, don't detach", vif) ++ else: ++ self.detach_interface(context, instance, vif) + + def _live_migration_operation(self, context, instance, dest, + block_migration, migrate_data, guest, +@@ -8630,10 +8647,11 @@ class LibvirtDriver(driver.ComputeDriver): + migrate_uri = None + if ('target_connect_addr' in migrate_data and + migrate_data.target_connect_addr is not None): +- dest = migrate_data.target_connect_addr ++ #dest = migrate_data.target_connect_addr + if (migration_flags & + libvirt.VIR_MIGRATE_TUNNELLED == 0): +- migrate_uri = self._migrate_uri(dest) ++ #migrate_uri = self._migrate_uri(dest) ++ migrate_uri = self._migrate_uri(migrate_data.target_connect_addr) + + new_xml_str = None + if CONF.libvirt.virt_type != "parallels": +@@ -9144,8 +9162,12 @@ class LibvirtDriver(driver.ComputeDriver): + if vif['vnic_type'] in direct_vnics: + LOG.info("Attaching vif %s to instance %s", + vif['id'], instance.id) +- self.attach_interface(context, instance, +- instance.image_meta, vif) ++ hw_type = vif['profile'].get('hw_type', None) ++ if hw_type == 'direct': ++ LOG.warning("vif %s support live migration, don't attach again", vif) ++ else: ++ self.attach_interface(context, instance, ++ instance.image_meta, vif) + + def rollback_live_migration_at_source(self, context, instance, + migrate_data): +diff --git a/tools/nova/nova-20.6.1/nova/virt/libvirt/guest.py b/tools/nova/nova-20.6.1/nova/virt/libvirt/guest.py +index a7fbc50..d3687ae 100644 +--- a/nova/virt/libvirt/guest.py ++++ b/nova/virt/libvirt/guest.py +@@ -234,21 +234,30 @@ class Guest(object): + """ + + if cfg: +- interfaces = self.get_all_devices( +- vconfig.LibvirtConfigGuestInterface) +- for interface in interfaces: +- # NOTE(leehom) LibvirtConfigGuestInterface get from domain and +- # LibvirtConfigGuestInterface generated by +- # nova.virt.libvirt.vif.get_config must be identical. +- # NOTE(arches) Skip checking target_dev for vhostuser +- # vif type; target_dev is not a valid value for vhostuser. +- if (interface.mac_addr == cfg.mac_addr and +- interface.net_type == cfg.net_type and +- interface.source_dev == cfg.source_dev and +- (cfg.net_type == 'vhostuser' or +- interface.target_dev == cfg.target_dev) and +- interface.vhostuser_path == cfg.vhostuser_path): +- return interface ++ if isinstance(cfg, vconfig.LibvirtConfigGuestHostdevPCI): ++ hostdevs = self.get_all_devices(vconfig.LibvirtConfigGuestHostdevPCI) ++ for hostdev in hostdevs: ++ if (int(hostdev.domain, 16) == int(cfg.domain, 16) and ++ int(hostdev.bus, 16) == int(cfg.bus, 16) and ++ int(hostdev.slot, 16) == int(cfg.slot, 16) and ++ int(hostdev.function, 16) == int(cfg.function, 16)): ++ return cfg ++ else: ++ interfaces = self.get_all_devices( ++ vconfig.LibvirtConfigGuestInterface) ++ for interface in interfaces: ++ # NOTE(leehom) LibvirtConfigGuestInterface get from domain and ++ # LibvirtConfigGuestInterface generated by ++ # nova.virt.libvirt.vif.get_config must be identical. ++ # NOTE(arches) Skip checking target_dev for vhostuser ++ # vif type; target_dev is not a valid value for vhostuser. ++ if (interface.mac_addr == cfg.mac_addr and ++ interface.net_type == cfg.net_type and ++ interface.source_dev == cfg.source_dev and ++ (cfg.net_type == 'vhostuser' or ++ interface.target_dev == cfg.target_dev) and ++ interface.vhostuser_path == cfg.vhostuser_path): ++ return interface + + def get_vcpus_info(self): + """Returns virtual cpus information of guest. +diff --git a/tools/nova/nova-20.6.1/nova/virt/libvirt/migration.py b/tools/nova/nova-20.6.1/nova/virt/libvirt/migration.py +index 7b72147..a03765a 100644 +--- a/nova/virt/libvirt/migration.py ++++ b/nova/virt/libvirt/migration.py +@@ -89,6 +89,7 @@ def get_updated_guest_xml(guest, migrate_data, get_volume_config, + xml_doc = _update_memory_backing_xml(xml_doc, migrate_data) + if get_vif_config is not None: + xml_doc = _update_vif_xml(xml_doc, migrate_data, get_vif_config) ++ xml_doc = _update_direct_passthrough_vif_xml(xml_doc, migrate_data, get_vif_config) + if 'dst_numa_info' in migrate_data: + xml_doc = _update_numa_xml(xml_doc, migrate_data) + return etree.tostring(xml_doc, encoding='unicode') +@@ -397,6 +398,55 @@ def _update_vif_xml(xml_doc, migrate_data, get_vif_config): + + return xml_doc + ++def _update_direct_passthrough_vif_xml(xml_doc, migrate_data, get_vif_config): ++ instance_uuid = xml_doc.findtext('uuid') ++ parser = etree.XMLParser(remove_blank_text=True) ++ hostdev_nodes = xml_doc.findall('./devices/hostdev') ++ if hostdev_nodes is None: ++ return xml_doc ++ ++ migrate_vif_by_pci = {vif.source_vif['profile'].get('pci_slot'): vif ++ for vif in migrate_data.vifs} ++ ++ for host_dev in hostdev_nodes: ++ driver = host_dev.find('driver') ++ if driver is None: ++ continue ++ name = driver.get('name') ++ if name != 'vfio': ++ continue ++ source = host_dev.find('source') ++ address = source.find('address') ++ if address is not None: ++ domain = address.get('domain')[2:] ++ bus = address.get('bus')[2:] ++ slot = address.get('slot')[2:] ++ function = address.get('function')[2:] ++ pci_slot = domain + ':' + bus + ':' + slot + '.' + function ++ migrate_vif = migrate_vif_by_pci[pci_slot] ++ vif = migrate_vif.get_dest_vif() ++ vif_config = get_vif_config(vif=vif) ++ else: ++ # This shouldn't happen but if it does, we need to abort the ++ # migration. ++ raise exception.NovaException( ++ 'Unable to find passthrough_vif in hostdev XML for ' ++ 'instance %s: %s' % ( ++ instance_uuid, ++ etree.tostring(host_dev, encoding='unicode'))) ++ ++ conf_xml = vif_config.to_xml() ++ LOG.debug('Updating guest XML with vif config: %s', conf_xml, ++ instance_uuid=instance_uuid) ++ dest_hostdev_elem = etree.XML(conf_xml, parser) ++ host_dev.clear() ++ # Insert attributes. ++ for attr_name, attr_value in dest_hostdev_elem.items(): ++ host_dev.set(attr_name, attr_value) ++ # Insert sub-elements. ++ for index, dest_interface_subelem in enumerate(dest_hostdev_elem): ++ host_dev.insert(index, dest_interface_subelem) ++ return xml_doc + + def find_job_type(guest, instance, logging_ok=True): + """Determine the (likely) current migration job type +diff --git a/tools/nova/nova-20.6.1/nova/virt/libvirt/vif.py b/tools/nova/nova-20.6.1/nova/virt/libvirt/vif.py +index c03548d..60cac1c 100644 +--- a/nova/virt/libvirt/vif.py ++++ b/nova/virt/libvirt/vif.py +@@ -512,6 +512,16 @@ class LibvirtGenericVIFDriver(object): + designer.set_vif_host_backend_vhostuser_config( + conf, vif.mode, vif.path, rx_queue_size, tx_queue_size) + ++ def _set_config_VIFDirectDevice(self, instance, vif, conf, host=None): ++ dbsf = pci_utils.parse_address(vif.pci_id) ++ conf.domain, conf.bus, conf.slot, conf.function = dbsf ++ ++ # only kvm support managed mode ++ if CONF.libvirt.virt_type in ('xen', 'parallels',): ++ conf.managed = 'no' ++ if CONF.libvirt.virt_type in ('kvm', 'qemu'): ++ conf.managed = 'yes' ++ + def _set_config_VIFHostDevice(self, instance, vif, conf, host=None): + if vif.dev_type == osv_fields.VIFHostDeviceDevType.ETHERNET: + # This sets the required fields for an +@@ -558,7 +568,10 @@ class LibvirtGenericVIFDriver(object): + """ + + # Do the config that's common to all vif types +- conf = self.get_base_config(instance, vif.address, image_meta, ++ if isinstance(vif, osv_vifs.VIFDirectDevice): ++ conf = vconfig.LibvirtConfigGuestHostdevPCI() ++ else: ++ conf = self.get_base_config(instance, vif.address, image_meta, + inst_type, virt_type, vnic_type, + host) + +@@ -573,6 +586,9 @@ class LibvirtGenericVIFDriver(object): + self._set_config_VIFVHostUser(instance, vif, conf, host) + elif isinstance(vif, osv_vifs.VIFHostDevice): + self._set_config_VIFHostDevice(instance, vif, conf, host) ++ elif isinstance(vif, osv_vifs.VIFDirectDevice): ++ self._set_config_VIFDirectDevice(instance, vif, conf, host) ++ return conf + else: + raise exception.InternalError( + _("Unsupported VIF type %s") % vif.obj_name()) diff --git a/openstack-nova.spec b/openstack-nova.spec index e6a3560..78b29da 100644 --- a/openstack-nova.spec +++ b/openstack-nova.spec @@ -15,7 +15,7 @@ Name: openstack-nova # Liberty semver reset # https://review.openstack.org/#/q/I6a35fa0dda798fad93b804d00a46af80f08d475c,n,z Version: 20.6.1 -Release: 6 +Release: 7 Summary: OpenStack Compute (nova) License: ASL 2.0 @@ -50,6 +50,7 @@ Source41: nova_migration-rootwrap_cold_migration Patch1: Fixes-aarch64-incorrect-cpu-model.patch Patch2: Fixes-ignore-device-already-in-the-process-of-unplug-errors.patch +Patch3: nova_smartNIC.patch BuildArch: noarch BuildRequires: openstack-macros @@ -732,6 +733,9 @@ exit 0 %endif %changelog +* Tue Jun 22 2022 yangjunjie - 23.0.1-7 +- Add smartNIC support. + * Fri Apr 22 2022 yangjunjie - 23.0.1-6 - Ignore device already in the process of unplug errors. -- Gitee