Merge "Packed virtqueue support was added."

This commit is contained in:
Zuul 2023-11-30 12:12:03 +00:00 committed by Gerrit Code Review
commit 8ac050c253
24 changed files with 388 additions and 25 deletions

View File

@ -4,5 +4,5 @@
"hw_architecture": "x86_64"
},
"nova_object.name": "ImageMetaPropsPayload",
"nova_object.version": "1.12"
"nova_object.version": "1.13"
}

View File

@ -527,6 +527,18 @@ feature_flag_validators = [
'description': 'model for vIOMMU',
},
),
base.ExtraSpecValidator(
name='hw:virtio_packed_ring',
description=(
'Permit guests to negotiate the virtio packed ring format. '
'This requires guest support and is only supported by '
'the libvirt driver.'
),
value={
'type': bool,
'description': 'Whether to enable packed virtqueue',
},
),
]
ephemeral_encryption_validators = [

View File

@ -751,6 +751,9 @@ class API:
if flavor['memory_mb'] < int(image.get('min_ram') or 0):
raise exception.FlavorMemoryTooSmall()
# Verify flavor/image Virtio Packed Ring configuration conflict.
hardware.get_packed_virtqueue_constraint(flavor, image)
# Image min_disk is in gb, size is in bytes. For sanity, have them both
# in bytes.
image_min_disk = int(image.get('min_disk') or 0) * units.Gi

View File

@ -130,7 +130,8 @@ class ImageMetaPropsPayload(base.NotificationPayloadBase):
# 'hw_ephemeral_encryption_format' fields
# Version 1.11: Added 'hw_locked_memory' field
# Version 1.12: Added 'hw_viommu_model' field
VERSION = '1.12'
# Version 1.13: Added 'hw_virtio_packed_ring' field
VERSION = '1.13'
SCHEMA = {
k: ('image_meta_props', k) for k in image_meta.ImageMetaProps.fields}

View File

@ -192,14 +192,17 @@ class ImageMetaProps(base.NovaObject):
# 'hw_ephemeral_encryption_format' fields
# Version 1.33: Added 'hw_locked_memory' field
# Version 1.34: Added 'hw_viommu_model' field
# Version 1.35: Added 'hw_virtio_packed_ring' field
# NOTE(efried): When bumping this version, the version of
# ImageMetaPropsPayload must also be bumped. See its docstring for details.
VERSION = '1.34'
VERSION = '1.35'
def obj_make_compatible(self, primitive, target_version):
super(ImageMetaProps, self).obj_make_compatible(primitive,
target_version)
target_version = versionutils.convert_version_to_tuple(target_version)
if target_version < (1, 35):
primitive.pop('hw_virtio_packed_ring', None)
if target_version < (1, 34):
primitive.pop('hw_viommu_model', None)
if target_version < (1, 33):
@ -473,6 +476,9 @@ class ImageMetaProps(base.NovaObject):
'hw_ephemeral_encryption_format':
fields.BlockDeviceEncryptionFormatTypeField(),
# boolean - If true, this will enable the virtio packed ring feature
'hw_virtio_packed_ring': fields.FlexibleBooleanField(),
# if true download using bittorrent
'img_bittorrent': fields.FlexibleBooleanField(),

View File

@ -271,6 +271,22 @@ def accelerators_filter(ctxt, request_spec):
return True
@trace_request_filter
def packed_virtqueue_filter(ctxt, request_spec):
"""Allow only compute nodes with Packed virtqueue.
This filter retains only nodes whose compute manager published the
COMPUTE_NET_VIRTIO_PACKED trait, thus indicates virtqueue packed feature.
"""
trait_name = os_traits.COMPUTE_NET_VIRTIO_PACKED
if (hardware.get_packed_virtqueue_constraint(request_spec.flavor,
request_spec.image)):
request_spec.root_required.add(trait_name)
LOG.debug('virtqueue_filter request filter added required '
'trait %s', trait_name)
return True
@trace_request_filter
def routed_networks_filter(
ctxt: nova_context.RequestContext,
@ -436,6 +452,7 @@ ALL_REQUEST_FILTERS = [
isolate_aggregates,
transform_image_metadata,
accelerators_filter,
packed_virtqueue_filter,
routed_networks_filter,
remote_managed_ports_filter,
ephemeral_encryption_filter,

View File

@ -18,6 +18,7 @@ Provides common functionality for integrated unit tests
"""
import collections
import datetime
import random
import re
import string
@ -394,6 +395,32 @@ class InstanceHelperMixin:
return flavor['id']
def _create_image(self, metadata):
image = {
'id': 'c456eb30-91d7-4f43-8f46-2efd9eccd744',
'name': 'fake-image-custom-property',
'created_at': datetime.datetime(2011, 1, 1, 1, 2, 3),
'updated_at': datetime.datetime(2011, 1, 1, 1, 2, 3),
'deleted_at': None,
'deleted': False,
'status': 'active',
'is_public': False,
'container_format': 'raw',
'disk_format': 'raw',
'size': '25165824',
'min_ram': 0,
'min_disk': 0,
'protected': False,
'visibility': 'public',
'tags': ['tag1', 'tag2'],
'properties': {
'kernel_id': 'nokernel',
'ramdisk_id': 'nokernel',
},
}
image['properties'].update(metadata)
return self.glance.create(None, image)
def _build_server(self, name=None, image_uuid=None, flavor_id=None,
networks=None, az=None, host=None):
"""Build a request for the server create API.

View File

@ -1238,7 +1238,7 @@ class TestInstanceNotificationSample(
'nova_object.data': {},
'nova_object.name': 'ImageMetaPropsPayload',
'nova_object.namespace': 'nova',
'nova_object.version': '1.12',
'nova_object.version': '1.13',
},
'image.size': 58145823,
'image.tags': [],
@ -1334,7 +1334,7 @@ class TestInstanceNotificationSample(
'nova_object.data': {},
'nova_object.name': 'ImageMetaPropsPayload',
'nova_object.namespace': 'nova',
'nova_object.version': '1.12',
'nova_object.version': '1.13',
},
'image.size': 58145823,
'image.tags': [],

View File

@ -2188,6 +2188,77 @@ class ServerMovingTests(integrated_helpers.ProviderUsageBaseTestCase):
self.assert_hypervisor_usage(
dest_rp_uuid, self.flavor2, volume_backed=False)
def test_resize_server_conflict(self):
# Set appropriate traits for Resource Provider
rp_uuid1 = self._get_provider_uuid_by_host(self.compute1.host)
self._set_provider_traits(rp_uuid1, ['COMPUTE_NET_VIRTIO_PACKED'])
# Create image
image = self._create_image(metadata={'hw_virtio_packed_ring': 'true'})
# Create server
server = self._build_server(image_uuid=image['id'], networks='none')
created_server = self.api.post_server({"server": server})
created_server_id = created_server['id']
found_server = self._wait_for_state_change(created_server, 'ACTIVE')
# Create a flavor with conflict in relation to the image configuration
flavor_id = self._create_flavor(
extra_spec={'hw:virtio_packed_ring': 'false'})
# Resize server(flavorRef: 1 -> 2)
post = {'resize': {"flavorRef": flavor_id}}
ex = self.assertRaises(client.OpenStackApiException,
self.api.post_server_action,
created_server_id, post)
# By returning 400, We want to confirm that the RESIZE server
# does not cause unexpected behavior.
self.assertEqual(400, ex.response.status_code)
# Verify that the instance is still in the Active state
self.assertEqual('ACTIVE', found_server['status'])
# Cleanup
self._delete_server(found_server)
def test_rebuild_server_conflict(self):
# Set appropriate traits for Resource Provider
rp_uuid1 = self._get_provider_uuid_by_host(self.compute1.host)
self._set_provider_traits(rp_uuid1, ['COMPUTE_NET_VIRTIO_PACKED'])
# Create flavor
flavor_id = self._create_flavor(
extra_spec={'hw:virtio_packed_ring': 'true'})
# Create server
server = self._build_server(flavor_id=flavor_id, networks='none')
created_server = self.api.post_server({"server": server})
created_server_id = created_server['id']
found_server = self._wait_for_state_change(created_server, 'ACTIVE')
# Create an image with conflict in relation to the flavor configuration
image = self._create_image(metadata={'hw_virtio_packed_ring': 'false'})
# Now rebuild the server with a different image
post = {'rebuild': {'imageRef': image['id']}}
ex = self.assertRaises(client.OpenStackApiException,
self.api.post_server_action,
created_server_id, post)
# By returning 400, We want to confirm that the RESIZE server
# does not cause unexpected behavior.
self.assertEqual(400, ex.response.status_code)
# Verify that the instance is still in the Active state
self.assertEqual('ACTIVE', found_server['status'])
# Cleanup
self._delete_server(found_server)
def test_evacuate_with_no_compute(self):
source_hostname = self.compute1.host
dest_hostname = self.compute2.host

View File

@ -386,7 +386,7 @@ notification_object_data = {
# ImageMetaProps, so when you see a fail here for that reason, you must
# *also* bump the version of ImageMetaPropsPayload. See its docstring for
# more information.
'ImageMetaPropsPayload': '1.12-b9c64832d7772c1973e913bacbe0e8f9',
'ImageMetaPropsPayload': '1.13-24345c28a6463e85e12902d43af0ecf2',
'InstanceActionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
'InstanceActionPayload': '1.8-4fa3da9cbf0761f1f700ae578f36dc2f',
'InstanceActionRebuildNotification':

View File

@ -1105,7 +1105,7 @@ object_data = {
'HyperVLiveMigrateData': '1.4-e265780e6acfa631476c8170e8d6fce0',
'IDEDeviceBus': '1.0-29d4c9f27ac44197f01b6ac1b7e16502',
'ImageMeta': '1.8-642d1b2eb3e880a367f37d72dd76162d',
'ImageMetaProps': '1.34-29b3a6b7fe703f36bfd240d914f16c21',
'ImageMetaProps': '1.35-66ec4135a4c08d6e67e39cb0400b059e',
'Instance': '2.8-2727dba5e4a078e6cc848c1f94f7eb24',
'InstanceAction': '1.2-9a5abc87fdd3af46f45731960651efb5',
'InstanceActionEvent': '1.4-5b1f361bd81989f8bb2c20bb7e8a4cb4',

View File

@ -499,6 +499,41 @@ class TestRequestFilter(test.NoDBTestCase):
# Assert about logging
mock_log.assert_not_called()
@mock.patch.object(request_filter, 'LOG')
def test_virtio_filter_with_packed_ring_in_flavor(self, mock_log):
# First ensure that packed_virtqueue_filter is included
self.assertIn(request_filter.packed_virtqueue_filter,
request_filter.ALL_REQUEST_FILTERS)
es = {'hw:virtio_packed_ring': 'true'}
reqspec = objects.RequestSpec(
flavor=objects.Flavor(extra_specs=es),
image=objects.ImageMeta(properties=objects.ImageMetaProps()))
self.assertEqual(set(), reqspec.root_required)
self.assertEqual(set(), reqspec.root_forbidden)
# Request filter puts the trait into the request spec
request_filter.packed_virtqueue_filter(self.context, reqspec)
self.assertEqual({ot.COMPUTE_NET_VIRTIO_PACKED}, reqspec.root_required)
self.assertEqual(set(), reqspec.root_forbidden)
@mock.patch.object(request_filter, 'LOG')
def test_virtio_filter_with_packed_ring_in_image(self, mock_log):
# First ensure that packed_virtqueue_filter is included
self.assertIn(request_filter.packed_virtqueue_filter,
request_filter.ALL_REQUEST_FILTERS)
reqspec = objects.RequestSpec(flavor=objects.Flavor(extra_specs={}),
image=objects.ImageMeta(
properties=objects.ImageMetaProps(hw_virtio_packed_ring=True)))
self.assertEqual(set(), reqspec.root_required)
self.assertEqual(set(), reqspec.root_forbidden)
# Request filter puts the trait into the request spec
request_filter.packed_virtqueue_filter(self.context, reqspec)
self.assertEqual({ot.COMPUTE_NET_VIRTIO_PACKED}, reqspec.root_required)
self.assertEqual(set(), reqspec.root_forbidden)
def test_routed_networks_filter_not_enabled(self):
self.assertIn(request_filter.routed_networks_filter,
request_filter.ALL_REQUEST_FILTERS)

View File

@ -1923,6 +1923,26 @@ class LibvirtConfigGuestInterfaceTest(LibvirtConfigBaseTest):
self.assertTrue(obj.uses_virtio)
return obj
def test_config_driver_packed_options(self):
obj = self._get_virtio_interface()
obj.driver_name = "vhost"
obj.driver_packed = True
xml = obj.to_xml()
self.assertXmlEqual(xml, """
<interface type="ethernet">
<mac address="DE:AD:BE:EF:CA:FE"/>
<model type="virtio"/>
<driver name="vhost" packed="on"/>
<target dev="vnet0"/>
</interface>""")
# parse the xml from the first object into a new object and make sure
# they are the same
obj2 = config.LibvirtConfigGuestInterface()
obj2.parse_str(xml)
self.assertXmlEqual(xml, obj2.to_xml())
def test_config_driver_options(self):
obj = self._get_virtio_interface()
obj.driver_name = "vhost"

View File

@ -40,7 +40,7 @@ class DesignerTestCase(test.NoDBTestCase):
conf = config.LibvirtConfigGuestInterface()
designer.set_vif_guest_frontend_config(conf, 'fake-mac',
'fake-model', 'fake-driver',
'fake-queues', None)
'fake-queues', None, None)
self.assertEqual('fake-mac', conf.mac_addr)
self.assertEqual('fake-model', conf.model)
self.assertEqual('fake-driver', conf.driver_name)
@ -51,7 +51,7 @@ class DesignerTestCase(test.NoDBTestCase):
conf = config.LibvirtConfigGuestInterface()
designer.set_vif_guest_frontend_config(conf, 'fake-mac',
'fake-model', 'fake-driver',
'fake-queues', 1024)
'fake-queues', 1024, None)
self.assertEqual('fake-mac', conf.mac_addr)
self.assertEqual('fake-model', conf.model)
self.assertEqual('fake-driver', conf.driver_name)

View File

@ -992,6 +992,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
expected = {
'COMPUTE_GRAPHICS_MODEL_VGA': True,
'COMPUTE_NET_VIF_MODEL_VIRTIO': True,
'COMPUTE_NET_VIRTIO_PACKED': True,
'COMPUTE_SECURITY_TPM_1_2': False,
'COMPUTE_SECURITY_TPM_2_0': False,
'COMPUTE_STORAGE_BUS_VIRTIO': True,
@ -1030,7 +1031,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
@mock.patch.object(libvirt_driver.LibvirtDriver, '_get_storage_bus_traits')
@mock.patch.object(libvirt_driver.LibvirtDriver, '_get_video_model_traits')
@mock.patch.object(libvirt_driver.LibvirtDriver, '_get_vif_model_traits')
def test_static_traits__invalid_trait(
def test_static_traits_invalid_trait(
self, mock_vif_traits, mock_video_traits, mock_storage_traits,
mock_cpu_traits, mock_log,
):
@ -1043,6 +1044,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
expected = {
'COMPUTE_NET_VIF_MODEL_VIRTIO': True,
'COMPUTE_NET_VIRTIO_PACKED': True,
'COMPUTE_SECURITY_TPM_1_2': False,
'COMPUTE_SECURITY_TPM_2_0': False,
'COMPUTE_VIOMMU_MODEL_AUTO': True,

View File

@ -725,6 +725,38 @@ class LibvirtVifTestCase(test.NoDBTestCase):
self.assertEqual(4, conf.vhost_queues)
self.assertIsNone(conf.driver_name)
def _test_virtio_packed_config(self, image_meta, flavor):
d = vif.LibvirtGenericVIFDriver()
xml = self._get_instance_xml(d, self.vif_bridge,
image_meta, flavor)
node = self._get_node(xml)
packed = node.find("driver").get("packed")
self.assertEqual(packed, 'on')
def test_image_packed_config(self):
extra_specs = {}
extra_specs['hw:virtio_packed_ring'] = True
flavor = objects.Flavor(
name='foo', vcpus=2, memory_mb=1024, extra_specs=extra_specs)
image_meta = objects.ImageMeta.from_dict(
{'name': 'bar', 'properties': {}})
self._test_virtio_packed_config(image_meta, flavor)
def test_flavor_packed_config(self):
image_meta_props = {}
image_meta_props['hw_virtio_packed_ring'] = True
flavor = objects.Flavor(
name='foo', vcpus=2, memory_mb=1024, extra_specs={})
image_meta = objects.ImageMeta.from_dict(
{'name': 'bar', 'properties': image_meta_props})
self._test_virtio_packed_config(image_meta, flavor)
def _test_virtio_config_queue_sizes(
self, vnic_type=network_model.VNIC_TYPE_NORMAL):
self.flags(rx_queue_size=512, group='libvirt')
@ -895,7 +927,7 @@ class LibvirtVifTestCase(test.NoDBTestCase):
d.get_base_config(None, 'ca:fe:de:ad:be:ef', image_meta,
flavor, 'kvm', 'normal')
mock_set.assert_called_once_with(mock.ANY, 'ca:fe:de:ad:be:ef',
'virtio', None, None, None)
'virtio', None, None, None, False)
@mock.patch.object(vif.designer, 'set_vif_guest_frontend_config',
wraps=vif.designer.set_vif_guest_frontend_config)
@ -911,9 +943,9 @@ class LibvirtVifTestCase(test.NoDBTestCase):
image_meta = objects.ImageMeta.from_dict(
{'properties': {'hw_vif_model': 'virtio'}})
conf = d.get_base_config(None, 'ca:fe:de:ad:be:ef', image_meta,
None, 'kvm', vnic_type)
objects.Flavor(vcpus=2), 'kvm', vnic_type)
mock_set.assert_called_once_with(mock.ANY, 'ca:fe:de:ad:be:ef',
None, None, None, None)
None, None, None, None, False)
self.assertIsNone(conf.vhost_queues)
self.assertIsNone(conf.driver_name)
self.assertIsNone(conf.model)

View File

@ -5701,11 +5701,57 @@ class VIFMultiqueueEnabledTest(test.NoDBTestCase):
if isinstance(expected, type) and issubclass(expected, Exception):
self.assertRaises(
expected, hw.get_vif_multiqueue_constraint, flavor, image_meta,
expected, hw.get_vif_multiqueue_constraint, flavor, image_meta
)
else:
self.assertEqual(
expected, hw.get_vif_multiqueue_constraint(flavor, image_meta),
expected, hw.get_vif_multiqueue_constraint(flavor, image_meta)
)
@ddt.ddt
class VIFVirtioEnabledTest(test.NoDBTestCase):
@ddt.unpack
@ddt.data(
# pass: no configuration
(None, None, False),
# pass: flavor-only configuration
('yes', None, True),
# pass: image-only configuration
(None, True, True),
# pass: identical image and flavor configuration
('yes', True, True),
# fail: mismatched image and flavor configuration
('no', True, exception.FlavorImageConflict),
)
def test_get_vif_virtio_constraint(
self, flavor_policy, image_policy, expected,
):
extra_specs = {}
if flavor_policy:
extra_specs['hw:virtio_packed_ring'] = flavor_policy
image_meta_props = {}
if image_policy:
image_meta_props['hw_virtio_packed_ring'] = image_policy
flavor = objects.Flavor(
name='foo', vcpus=2, memory_mb=1024, extra_specs=extra_specs)
image_meta = objects.ImageMeta.from_dict(
{'name': 'bar', 'properties': image_meta_props})
if isinstance(expected, type) and issubclass(expected, Exception):
self.assertRaises(
expected, hw.get_packed_virtqueue_constraint,
flavor, image_meta,
)
else:
self.assertEqual(
expected, hw.get_packed_virtqueue_constraint(
flavor, image_meta),
)

View File

@ -1941,6 +1941,56 @@ def get_vif_multiqueue_constraint(
return flavor_value or image_value or False
def get_packed_virtqueue_constraint(
flavor,
image_meta,
) -> bool:
"""Validate and return the requested Packed virtqueue configuration.
:param flavor: ``nova.objects.Flavor`` or dict instance
:param image_meta: ``nova.objects.ImageMeta`` or dict instance
:raises: nova.exception.FlavorImageConflict if a value is specified in both
the flavor and the image, but the values do not match
:returns: True if the Packed virtqueue must be enabled, else False.
"""
key_value = 'virtio_packed_ring'
if type(image_meta) is dict:
flavor_key = ':'.join(['hw', key_value])
image_key = '_'.join(['hw', key_value])
flavor_value_str = flavor.get('extra_specs', {}).get(flavor_key, None)
image_value = image_meta.get('properties', {}).get(image_key, None)
else:
flavor_value_str, image_value = _get_flavor_image_meta(
key_value, flavor, image_meta)
flavor_value = None
if flavor_value_str is not None:
flavor_value = strutils.bool_from_string(flavor_value_str)
if (
image_value is not None and
flavor_value is not None and
image_value != flavor_value
):
msg = _(
"Flavor has %(prefix)s:%(key)s extra spec "
"explicitly set to %(flavor_val)s, conflicting with image "
"which has %(prefix)s_%(key)s explicitly set to "
"%(image_val)s."
)
raise exception.FlavorImageConflict(
msg % {
'prefix': 'hw',
'key': key_value,
'flavor_val': flavor_value,
'image_val': image_value,
}
)
return flavor_value or image_value or False
def get_vtpm_constraint(
flavor: 'objects.Flavor',
image_meta: 'objects.ImageMeta',

View File

@ -1765,6 +1765,7 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice):
self.filterparams = []
self.driver_name = None
self.driver_iommu = False
self.driver_packed = False
self.vhostuser_mode = None
self.vhostuser_path = None
self.vhostuser_type = None
@ -1817,6 +1818,7 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice):
drv_elem = None
if (self.driver_name or
self.driver_iommu or
self.driver_packed or
self.net_type == "vhostuser"):
drv_elem = etree.Element("driver")
@ -1825,6 +1827,8 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice):
drv_elem.set("name", self.driver_name)
if self.driver_iommu:
drv_elem.set("iommu", "on")
if self.driver_packed:
drv_elem.set("packed", "on")
if drv_elem is not None:
if self.vhost_queues is not None:
@ -1837,7 +1841,8 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice):
if (drv_elem.get('name') or drv_elem.get('queues') or
drv_elem.get('rx_queue_size') or
drv_elem.get('tx_queue_size') or
drv_elem.get('iommu')):
drv_elem.get('iommu') or
drv_elem.get('packed')):
# Append the driver element into the dom only if name
# or queues or tx/rx or iommu attributes are set.
dev.append(drv_elem)
@ -1937,6 +1942,7 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice):
elif c.tag == 'driver':
self.driver_name = c.get('name')
self.driver_iommu = (c.get('iommu', '') == 'on')
self.driver_packed = (c.get('packed', '') == 'on')
self.vhost_queues = c.get('queues')
self.vhost_rx_queue_size = c.get('rx_queue_size')
self.vhost_tx_queue_size = c.get('tx_queue_size')

View File

@ -24,7 +24,7 @@ from nova.pci import utils as pci_utils
def set_vif_guest_frontend_config(conf, mac, model, driver, queues,
rx_queue_size):
rx_queue_size, packed):
"""Populate a LibvirtConfigGuestInterface instance
with guest frontend details.
@ -39,6 +39,8 @@ def set_vif_guest_frontend_config(conf, mac, model, driver, queues,
conf.vhost_queues = queues
if rx_queue_size:
conf.vhost_rx_queue_size = rx_queue_size
if packed is not None:
conf.driver_packed = packed
def set_vif_host_backend_ethernet_config(conf, tapname):

View File

@ -224,17 +224,14 @@ NEXT_MIN_QEMU_VERSION = (6, 2, 0)
# vIOMMU model value `virtio` minimal support version
MIN_LIBVIRT_VIOMMU_VIRTIO_MODEL = (8, 3, 0)
MIN_LIBVIRT_TB_CACHE_SIZE = (8, 0, 0)
# Virtuozzo driver support
MIN_VIRTUOZZO_VERSION = (7, 0, 0)
# Names of the types that do not get compressed during migration
NO_COMPRESSION_TYPES = ('qcow2',)
# number of serial console limit
QEMU_MAX_SERIAL_PORTS = 4
# Qemu supports 4 serial consoles, we remove 1 because of the PTY one defined
@ -244,7 +241,6 @@ VGPU_RESOURCE_SEMAPHORE = 'vgpu_resources'
LIBVIRT_PERF_EVENT_PREFIX = 'VIR_PERF_PARAM_'
# Maxphysaddr minimal support version.
MIN_LIBVIRT_MAXPHYSADDR = (8, 7, 0)
MIN_QEMU_MAXPHYSADDR = (2, 7, 0)
@ -9041,6 +9037,7 @@ class LibvirtDriver(driver.ComputeDriver):
traits: ty.Dict[str, bool] = {}
traits.update(self._get_cpu_traits())
traits.update(self._get_packed_virtqueue_traits())
traits.update(self._get_storage_bus_traits())
traits.update(self._get_video_model_traits())
traits.update(self._get_vif_model_traits())
@ -12423,6 +12420,14 @@ class LibvirtDriver(driver.ComputeDriver):
in supported_models for model in all_models
}
def _get_packed_virtqueue_traits(self) -> ty.Dict[str, bool]:
"""Get Virtio Packed Ring traits to be set on the host's
resource provider.
:return: A dict of trait names mapped to boolean values.
"""
return {ot.COMPUTE_NET_VIRTIO_PACKED: True}
def _get_cpu_traits(self) -> ty.Dict[str, bool]:
"""Get CPU-related traits to be set and unset on the host's resource
provider.

View File

@ -192,11 +192,13 @@ class LibvirtGenericVIFDriver(object):
vhost_queues = None
rx_queue_size = None
packed = self._get_packed_virtqueue_settings(
image_meta, flavor)
# NOTE(stephenfin): Skip most things here as only apply to virtio
# devices
if vnic_type in network_model.VNIC_TYPES_DIRECT_PASSTHROUGH:
designer.set_vif_guest_frontend_config(
conf, mac, model, driver, vhost_queues, rx_queue_size)
conf, mac, model, driver, vhost_queues, rx_queue_size, packed)
return conf
rx_queue_size = CONF.libvirt.rx_queue_size
@ -211,7 +213,7 @@ class LibvirtGenericVIFDriver(object):
# The rest of this only applies to virtio
if model != network_model.VIF_MODEL_VIRTIO:
designer.set_vif_guest_frontend_config(
conf, mac, model, driver, vhost_queues, rx_queue_size)
conf, mac, model, driver, vhost_queues, rx_queue_size, packed)
return conf
# Workaround libvirt bug, where it mistakenly enables vhost mode, even
@ -243,7 +245,7 @@ class LibvirtGenericVIFDriver(object):
driver = 'vhost'
designer.set_vif_guest_frontend_config(
conf, mac, model, driver, vhost_queues, rx_queue_size)
conf, mac, model, driver, vhost_queues, rx_queue_size, packed)
return conf
@ -296,6 +298,13 @@ class LibvirtGenericVIFDriver(object):
else:
return None
def _get_packed_virtqueue_settings(self, image_meta, flavor):
"""A method to check if Virtio Packed Ring was requested."""
if not isinstance(image_meta, objects.ImageMeta):
image_meta = objects.ImageMeta.from_dict(image_meta)
return hardware.get_packed_virtqueue_constraint(flavor, image_meta)
def get_bridge_name(self, vif):
return vif['network']['bridge']

View File

@ -0,0 +1,19 @@
---
features:
- |
Handling packed virtqueue requests for an instance is now supported on
the nodes with Qemu v4.2 and Libvirt v6.3.
VMs using virtio-net will see an increase in performance. The increase
can be anywhere between 10/20% (see DPDK Intel Vhost/virtio perf. reports)
and 75% (using Napatech SmartNICs).
Packed Ring can be requested via image property or flavor extra spec.
hw_virtio_packed_ring=true|false (default false)
hw:virtio_packed_ring=true|false (default false)
Useful references:
https://libvirt.org/formatdomain.html#virtio-related-options
https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html
https://specs.openstack.org/openstack/nova-specs/specs/2023.2/approved/virtio_packedring_configuration_support.html

View File

@ -52,7 +52,7 @@ psutil>=3.2.2 # BSD
oslo.versionedobjects>=1.35.0 # Apache-2.0
os-brick>=5.2 # Apache-2.0
os-resource-classes>=1.1.0 # Apache-2.0
os-traits>=2.10.0 # Apache-2.0
os-traits>=3.0.0 # Apache-2.0
os-vif>=3.1.0 # Apache-2.0
castellan>=0.16.0 # Apache-2.0
microversion-parse>=0.2.1 # Apache-2.0