Skip to content

Commit f5aa144

Browse files
committed
hardware: Allow 'hw:cpu_realtime_mask' to be omitted
By offloading emulator threads to other host cores (via the 'hw:emulator_threads_policy' extra spec), it's possible to allocate all guest cores to realtime. Enable this. Part of blueprint use-pcpu-and-vcpu-in-one-instance Change-Id: I00805cf9cca9657955c7e1ef3a76e384adaa78f1 Signed-off-by: Stephen Finucane <[email protected]>
1 parent d689533 commit f5aa144

File tree

7 files changed

+84
-11
lines changed

7 files changed

+84
-11
lines changed

doc/source/user/flavors.rst

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -601,8 +601,9 @@ CPU real-time policy
601601
.. important::
602602

603603
While most of your instance vCPUs can run with a real-time policy, you must
604-
mark at least one vCPU as non-real-time, to be used for both non-real-time
605-
guest processes and emulator overhead (housekeeping) processes.
604+
either mark at least one vCPU as non-real-time to be account for emulator
605+
overhead (housekeeping) or explicitly configure an :ref:`emulator thread
606+
policy <extra-specs-emulator-threads-policy>`.
606607

607608
.. important::
608609

@@ -633,6 +634,13 @@ CPU real-time policy
633634
The ``hw:cpu_realtime_mask`` option is only valid if ``hw:cpu_realtime``
634635
is set to ``yes``.
635636

637+
.. versionchanged:: 22.0.0 (Victoria)
638+
639+
Previously, it was necessary to specify ``hw:cpu_realtime_mask`` when
640+
``hw:cpu_realtime`` was set to yes. Starting in Victoria, it is possible
641+
to omit this when an emulator thread policy is configured using the
642+
``hw:emulator_threads_policy`` extra spec.
643+
636644
.. _extra-specs-emulator-threads-policy:
637645

638646
Emulator threads policy

nova/exception.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,9 +1894,8 @@ class LibguestfsCannotReadKernel(Invalid):
18941894

18951895

18961896
class RealtimeMaskNotFoundOrInvalid(Invalid):
1897-
msg_fmt = _("Realtime policy needs vCPU(s) mask configured with at least "
1898-
"1 RT vCPU and 1 ordinary vCPU. See hw:cpu_realtime_mask "
1899-
"or hw_cpu_realtime_mask")
1897+
msg_fmt = _("Use of realtime CPUs requires either one or more "
1898+
"non-realtime CPU(s) or offloaded emulator threads.")
19001899

19011900

19021901
class OsInfoNotFound(NotFound):

nova/tests/unit/virt/libvirt/test_driver.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3027,6 +3027,34 @@ def test_get_guest_memory_backing_config_realtime(self):
30273027
self.assertTrue(membacking.locked)
30283028
self.assertFalse(membacking.sharedpages)
30293029

3030+
def test_get_guest_memory_backing_config_realtime_invalid_share(self):
3031+
"""Test behavior when there is no pool of shared CPUS on which to place
3032+
the emulator threads, isolating them from the instance CPU processes.
3033+
"""
3034+
extra_specs = {
3035+
"hw:cpu_realtime": "yes",
3036+
"hw:cpu_policy": "dedicated",
3037+
"hw:emulator_threads_policy": "share",
3038+
}
3039+
flavor = objects.Flavor(
3040+
name='m1.small',
3041+
memory_mb=6,
3042+
vcpus=28,
3043+
root_gb=496,
3044+
ephemeral_gb=8128,
3045+
swap=33550336,
3046+
extra_specs=extra_specs)
3047+
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
3048+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
3049+
3050+
# this should fail because there is nowhere to place the emulator
3051+
# threads
3052+
self.assertRaises(
3053+
exception.RealtimeMaskNotFoundOrInvalid,
3054+
drvr._get_guest_memory_backing_config,
3055+
None, None, flavor, image_meta,
3056+
)
3057+
30303058
def _test_sev_enabled(self, expected=None, host_sev_enabled=False,
30313059
enc_extra_spec=None, enc_image_prop=None,
30323060
hw_machine_type=None, hw_firmware_type=None):

nova/tests/unit/virt/test_hardware.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3887,6 +3887,18 @@ def test_invalid_mask_no_exclusion_wo_emulator_policy(self):
38873887
exception.RealtimeMaskNotFoundOrInvalid,
38883888
hw.get_realtime_cpu_constraint, flavor, image)
38893889

3890+
def test_all_cpus_w_emulator_policy(self):
3891+
# The mask has no exclusion but there's an emulator thread policy
3892+
flavor = objects.Flavor(
3893+
vcpus=3, memory_mb=2048, extra_specs={
3894+
'hw:cpu_realtime': 'true',
3895+
'hw:emulator_threads_policy': 'isolate'
3896+
},
3897+
)
3898+
image = objects.ImageMeta.from_dict({"properties": {}})
3899+
rt = hw.get_realtime_cpu_constraint(flavor, image)
3900+
self.assertEqual({0, 1, 2}, rt)
3901+
38903902
def test_invalid_mask_rt_cpus_out_of_range(self):
38913903
# The mask is not just an exclusion mask, and the RT range specifies
38923904
# an invalid vCPU number.

nova/virt/hardware.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,16 +1705,19 @@ def get_realtime_cpu_constraint(
17051705
# Image masks are used ahead of flavor masks as they will have more
17061706
# specific requirements
17071707
mask = image_mask or flavor_mask
1708-
if not mask:
1709-
raise exception.RealtimeMaskNotFoundOrInvalid()
17101708

17111709
vcpus_set = set(range(flavor.vcpus))
1712-
vcpus_rt = parse_cpu_spec("0-%d,%s" % (flavor.vcpus - 1, mask))
1710+
if mask:
1711+
vcpus_rt = parse_cpu_spec("0-%d,%s" % (flavor.vcpus - 1, mask))
1712+
else:
1713+
vcpus_rt = set(range(flavor.vcpus))
17131714

17141715
if not vcpus_rt:
17151716
raise exception.RealtimeMaskNotFoundOrInvalid()
17161717

1717-
if vcpus_set == vcpus_rt:
1718+
# TODO(stephenfin): Do this check in numa_get_constraints instead
1719+
emu_policy = get_emulator_thread_policy_constraint(flavor)
1720+
if vcpus_set == vcpus_rt and not emu_policy:
17181721
raise exception.RealtimeMaskNotFoundOrInvalid()
17191722

17201723
if not vcpus_rt.issubset(vcpus_set):

nova/virt/libvirt/driver.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5432,15 +5432,33 @@ def _set_qemu_guest_agent(self, guest, flavor, instance, image_meta):
54325432

54335433
def _get_guest_memory_backing_config(
54345434
self, inst_topology, numatune, flavor, image_meta):
5435+
wantsrealtime = hardware.is_realtime_enabled(flavor)
5436+
if (
5437+
wantsrealtime and
5438+
hardware.get_emulator_thread_policy_constraint(flavor) ==
5439+
fields.CPUEmulatorThreadsPolicy.SHARE and
5440+
not CONF.compute.cpu_shared_set
5441+
):
5442+
# NOTE(stephenfin) Yes, it's horrible that we're doing this here,
5443+
# but the shared policy unfortunately has different behavior
5444+
# depending on whether the '[compute] cpu_shared_set' is configured
5445+
# or not and we need it to be configured. Also note that we have
5446+
# already handled other conditions, such as no emulator thread
5447+
# policy being configured whatsoever, at the API level.
5448+
LOG.warning(
5449+
'Instance is requesting real-time CPUs with pooled '
5450+
'emulator threads, but a shared CPU pool has not been '
5451+
'configured on this host.'
5452+
)
5453+
raise exception.RealtimeMaskNotFoundOrInvalid()
5454+
54355455
wantsmempages = False
54365456
if inst_topology:
54375457
for cell in inst_topology.cells:
54385458
if cell.pagesize:
54395459
wantsmempages = True
54405460
break
54415461

5442-
wantsrealtime = hardware.is_realtime_enabled(flavor)
5443-
54445462
wantsfilebacked = CONF.libvirt.file_backed_memory > 0
54455463

54465464
if wantsmempages and wantsfilebacked:

releasenotes/notes/bug-1884231-16acf297d88b122e.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,8 @@ fixes:
44
Previously, it was possible to specify values for the
55
``hw:cpu_realtime_mask`` extra spec that were not within the range of valid
66
instances cores. This value is now correctly validated.
7+
features:
8+
- |
9+
It is now possible to allocate all cores in an instance to realtime and
10+
omit the ``hw:cpu_realtime_mask`` extra spec. This requires specifying the
11+
``hw:emulator_threads_policy`` extra spec.

0 commit comments

Comments
 (0)