diff --git a/nova/conf/libvirt.py b/nova/conf/libvirt.py index c86532550611..0d218807d274 100644 --- a/nova/conf/libvirt.py +++ b/nova/conf/libvirt.py @@ -530,28 +530,49 @@ would result in an error and the instance launch will fail. cfg.ListOpt( 'cpu_model_extra_flags', item_type=types.String( - choices=['pcid'], ignore_case=True, ), default=[], help=""" -This allows specifying granular CPU feature flags when specifying CPU +This allows specifying granular CPU feature flags when configuring CPU models. For example, to explicitly specify the ``pcid`` -(Process-Context ID, an Intel processor feature) flag to the "IvyBridge" -virtual CPU model:: +(Process-Context ID, an Intel processor feature -- which is now required +to address the guest performance degradation as a result of applying the +"Meltdown" CVE fixes to certain Intel CPU models) flag to the +"IvyBridge" virtual CPU model:: [libvirt] cpu_mode = custom cpu_model = IvyBridge cpu_model_extra_flags = pcid -Currently, the choice is restricted to only one option: ``pcid`` (the -option is case-insensitive, so ``PCID`` is also valid). This flag is -now required to address the guest performance degradation as a result of -applying the "Meltdown" CVE fixes on certain Intel CPU models. +To specify multiple CPU flags (e.g. the Intel ``VMX`` to expose the +virtualization extensions to the guest, or ``pdpe1gb`` to configure 1GB +huge pages for CPU models that do not provide it): -Note that when using this config attribute to set the 'PCID' CPU flag, -not all virtual (i.e. libvirt / QEMU) CPU models need it: + [libvirt] + cpu_mode = custom + cpu_model = Haswell-noTSX-IBRS + cpu_model_extra_flags = PCID, VMX, pdpe1gb + +As it can be noticed from above, the ``cpu_model_extra_flags`` config +attribute is case insensitive. And specifying extra flags is valid in +combination with all the three possible values for ``cpu_mode``: +``custom`` (this also requires an explicit ``cpu_model`` to be +specified), ``host-model``, or ``host-passthrough``. A valid example +for allowing extra CPU flags even for ``host-passthrough`` mode is that +sometimes QEMU may disable certain CPU features -- e.g. Intel's +"invtsc", Invariable Time Stamp Counter, CPU flag. And if you need to +expose that CPU flag to the Nova instance, the you need to explicitly +ask for it. + +The possible values for ``cpu_model_extra_flags`` depends on the CPU +model in use. Refer to ``/usr/share/libvirt/cpu_map.xml`` possible CPU +feature flags for a given CPU model. + +Note that when using this config attribute to set the 'PCID' CPU flag +with the ``custom`` CPU mode, not all virtual (i.e. libvirt / QEMU) CPU +models need it: * The only virtual CPU models that include the 'PCID' capability are Intel "Haswell", "Broadwell", and "Skylake" variants. @@ -561,18 +582,14 @@ not all virtual (i.e. libvirt / QEMU) CPU models need it: even if the host CPUs by the same name include it. I.e. 'PCID' needs to be explicitly specified when using the said virtual CPU models. -For now, the ``cpu_model_extra_flags`` config attribute is valid only in -combination with ``cpu_mode`` + ``cpu_model`` options. - -Besides ``custom``, the libvirt driver has two other CPU modes: The -default, ``host-model``, tells it to do the right thing with respect to -handling 'PCID' CPU flag for the guest -- *assuming* you are running -updated processor microcode, host and guest kernel, libvirt, and QEMU. -The other mode, ``host-passthrough``, checks if 'PCID' is available in -the hardware, and if so directly passes it through to the Nova guests. -Thus, in context of 'PCID', with either of these CPU modes -(``host-model`` or ``host-passthrough``), there is no need to use the -``cpu_model_extra_flags``. +The libvirt driver's default CPU mode, ``host-model``, will do the right +thing with respect to handling 'PCID' CPU flag for the guest -- +*assuming* you are running updated processor microcode, host and guest +kernel, libvirt, and QEMU. The other mode, ``host-passthrough``, checks +if 'PCID' is available in the hardware, and if so directly passes it +through to the Nova guests. Thus, in context of 'PCID', with either of +these CPU modes (``host-model`` or ``host-passthrough``), there is no +need to use the ``cpu_model_extra_flags``. Related options: diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index f83868c6c544..23280df11265 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -6372,14 +6372,15 @@ class LibvirtConnTestCase(test.NoDBTestCase, mock_warn.assert_not_called() @mock.patch.object(libvirt_driver.LOG, 'warning') - def test_get_guest_cpu_config_host_model_with_extra_flags(self, + def test_get_guest_cpu_config_custom_with_multiple_extra_flags(self, mock_warn): drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = objects.Instance(**self.test_instance) image_meta = objects.ImageMeta.from_dict(self.test_image_meta) - self.flags(cpu_mode="host-model", - cpu_model_extra_flags="pcid", + self.flags(cpu_mode="custom", + cpu_model="IvyBridge", + cpu_model_extra_flags=['pcid', 'vmx'], group='libvirt') disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type, instance_ref, @@ -6387,14 +6388,45 @@ class LibvirtConnTestCase(test.NoDBTestCase, conf = drvr._get_guest_config(instance_ref, _fake_network_info(self, 1), image_meta, disk_info) + features = [feature.name for feature in conf.cpu.features] self.assertIsInstance(conf.cpu, vconfig.LibvirtConfigGuestCPU) - self.assertEqual(conf.cpu.mode, "host-model") - self.assertEqual(len(conf.cpu.features), 0) + self.assertEqual(conf.cpu.mode, "custom") + self.assertEqual(conf.cpu.model, "IvyBridge") + self.assertIn("pcid", features) + self.assertIn("vmx", features) self.assertEqual(conf.cpu.sockets, instance_ref.flavor.vcpus) self.assertEqual(conf.cpu.cores, 1) self.assertEqual(conf.cpu.threads, 1) - self.assertTrue(mock_warn.called) + mock_warn.assert_not_called() + + @mock.patch.object(libvirt_driver.LOG, 'warning') + def test_get_guest_cpu_config_host_model_with_extra_flags(self, + mock_warn): + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + instance_ref = objects.Instance(**self.test_instance) + image_meta = objects.ImageMeta.from_dict(self.test_image_meta) + + self.flags(cpu_mode="host-model", + cpu_model_extra_flags="pdpe1gb", + group='libvirt') + disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type, + instance_ref, + image_meta) + conf = drvr._get_guest_config(instance_ref, + _fake_network_info(self, 1), + image_meta, disk_info) + features = [feature.name for feature in conf.cpu.features] + self.assertIsInstance(conf.cpu, + vconfig.LibvirtConfigGuestCPU) + self.assertEqual(conf.cpu.mode, "host-model") + self.assertIn("pdpe1gb", features) + self.assertEqual(conf.cpu.sockets, instance_ref.flavor.vcpus) + self.assertEqual(conf.cpu.cores, 1) + self.assertEqual(conf.cpu.threads, 1) + # For 'host-model', it is now valid to use 'extra_flags'; + # assert that no warning is thrown + mock_warn.assert_not_called() @mock.patch.object(libvirt_driver.LOG, 'warning') def test_get_guest_cpu_config_host_passthrough_with_extra_flags(self, @@ -6404,7 +6436,7 @@ class LibvirtConnTestCase(test.NoDBTestCase, image_meta = objects.ImageMeta.from_dict(self.test_image_meta) self.flags(cpu_mode="host-passthrough", - cpu_model_extra_flags="pcid", + cpu_model_extra_flags="invtsc", group='libvirt') disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type, instance_ref, @@ -6412,14 +6444,17 @@ class LibvirtConnTestCase(test.NoDBTestCase, conf = drvr._get_guest_config(instance_ref, _fake_network_info(self, 1), image_meta, disk_info) + features = [feature.name for feature in conf.cpu.features] self.assertIsInstance(conf.cpu, vconfig.LibvirtConfigGuestCPU) self.assertEqual(conf.cpu.mode, "host-passthrough") - self.assertEqual(len(conf.cpu.features), 0) + self.assertIn("invtsc", features) self.assertEqual(conf.cpu.sockets, instance_ref.flavor.vcpus) self.assertEqual(conf.cpu.cores, 1) self.assertEqual(conf.cpu.threads, 1) - self.assertTrue(mock_warn.called) + # We have lifted the restriction for 'host-passthrough' as well; + # so here too, assert that no warning is thrown + mock_warn.assert_not_called() def test_get_guest_cpu_topology(self): instance_ref = objects.Instance(**self.test_instance) diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index a494c78ee999..d9eab437d5db 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -3815,25 +3815,6 @@ class LibvirtDriver(driver.ComputeDriver): msg = _("A CPU model name should not be set when a " "host CPU model is requested") raise exception.Invalid(msg) - # FIXME (kchamart): We're intentionally restricting the choices - # (in the conf/libvirt.py) for 'extra_flags` to just 'PCID', to - # address the immediate guest performance degradation caused by - # "Meltdown" CVE fixes on certain Intel CPU models. In a future - # patch, we will: - # (a) Remove the restriction of choices for 'extra_flags', - # allowing to add / remove additional CPU flags, as it will - # make way for other useful features. - # (b) Remove the below check for "host-model", as it is a - # valid configuration to supply additional CPU flags to it. - # (c) Revisit and fix the warnings / exception handling for - # different combinations of CPU modes and 'extra_flags'. - elif ((mode == "host-model" or mode == "host-passthrough") and - extra_flags): - extra_flags = [] - LOG.warning("Setting extra CPU flags is only valid in " - "combination with a custom CPU model. Refer " - "to the 'nova.conf' documentation for " - "'[libvirt]/cpu_model_extra_flags'") LOG.debug("CPU mode '%(mode)s' model '%(model)s' was chosen, " "with extra flags: '%(extra_flags)s'", diff --git a/releasenotes/notes/libvirt-cpu-model-extra-flags-a23085f58bd22d27.yaml b/releasenotes/notes/libvirt-cpu-model-extra-flags-a23085f58bd22d27.yaml index 14156df964e6..9e69275b2c9e 100644 --- a/releasenotes/notes/libvirt-cpu-model-extra-flags-a23085f58bd22d27.yaml +++ b/releasenotes/notes/libvirt-cpu-model-extra-flags-a23085f58bd22d27.yaml @@ -3,9 +3,11 @@ features: - | The libvirt driver now allows specifying individual CPU feature flags for guests, via a new configuration attribute - ``[libvirt]/cpu_model_extra_flags`` -- only with ``custom`` as the - ``[libvirt]/cpu_model``. Refer to its documentation in - ``nova.conf`` for usage details. + ``[libvirt]/cpu_model_extra_flags`` -- this is valid in combination + with all the three possible values for ``[libvirt]/cpu_mode``: + ``custom``, ``host-model``, or ``host-passthrough``. The + ``cpu_model_extra_flags`` also allows specifying multiple CPU flags. + Refer to its documentation in ``nova.conf`` for usage details. One of the motivations for this is to alleviate the performance degradation (caused as a result of applying the "Meltdown" CVE