diff --git a/doc/source/admin/cpu-topologies.rst b/doc/source/admin/cpu-topologies.rst index fdb2d7b59ef6..07d8e400d95c 100644 --- a/doc/source/admin/cpu-topologies.rst +++ b/doc/source/admin/cpu-topologies.rst @@ -221,10 +221,12 @@ use pinned CPUs. To configure a flavor to use the ``shared`` CPU policy, run: $ openstack flavor set [FLAVOR_ID] --property hw:cpu_policy=shared The ``mixed`` CPU policy is used to specify that an instance use pinned CPUs -along with unpinned CPUs. The instance pinned CPU is specified in the -``hw:cpu_dedicated_mask`` extra spec. For example, to configure a flavor to -use the ``mixed`` CPU policy with 4 vCPUs in total and the first 2 vCPUs as -pinned CPUs: +along with unpinned CPUs. The instance pinned CPU could be specified in the +``hw:cpu_dedicated_mask`` or, if real-time is enabled +(``hw:cpu_realtime``\ = yes), in the ``hw:cpu_realtime_mask`` extra spec. For +example, to configure a flavor to use the ``mixed`` CPU policy with 4 vCPUs in +total and the first 2 vCPUs as pinned CPUs, with the ``hw:cpu_realtime_mask`` +extra spec, run: .. code-block:: console @@ -233,13 +235,21 @@ pinned CPUs: --property hw:cpu_policy=mixed \ --property hw:cpu_dedicated_mask=0-1 -For more information about the syntax for ``hw:cpu_dedicated_mask``, refer -to the :doc:`/user/flavors` guide. +To create the mixed instance with the real-time extra specs, run: + +.. code-block:: console + + $ openstack flavor set [FLAVOR_ID] \ + --vcpus=4 \ + --property hw:cpu_policy=mixed \ + --property hw:cpu_realtime=yes \ + --property hw:cpu_realtime_mask=0-1 .. note:: - For more information about the syntax for ``hw:cpu_policy``, refer to the - :doc:`/admin/flavors` guide. + For more information about the syntax for ``hw:cpu_policy``, + ``hw:cpu_dedicated_mask``, ``hw:realtime_cpu`` and ``hw:cpu_realtime_mask``, + refer to the :doc:`/user/flavors` guide. It is also possible to configure the CPU policy via image metadata. This can be useful when packaging applications that require real-time or near real-time diff --git a/doc/source/user/flavors.rst b/doc/source/user/flavors.rst index a878c158a2d2..2d9e7ae9284e 100644 --- a/doc/source/user/flavors.rst +++ b/doc/source/user/flavors.rst @@ -467,7 +467,8 @@ CPU pinning policy .. note:: The ``hw:cpu_dedicated_mask`` option is only valid if ``hw:cpu_policy`` - is set to ``mixed``. + is set to ``mixed`` and cannot be configured with ``hw:cpu_realtime_mask`` + at the same time. Valid CPU-THREAD-POLICY values are: diff --git a/nova/exception.py b/nova/exception.py index 067cef473cb9..297d8a6a5f29 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -2326,9 +2326,11 @@ class RequiredMixedInstancePolicy(Invalid): class RequiredMixedOrRealtimeCPUMask(Invalid): - msg_fmt = _("Must specify either 'hw:cpu_dedicated_mask' or " - "'hw:cpu_realtime_mask' when using 'mixed' CPU policy" - " instance.") + msg_fmt = _("Dedicated CPU set can be specified from either " + "'hw:cpu_dedicated_mask' or 'hw:cpu_realtime_mask' when " + "using 'mixed' CPU policy. 'hw:cpu_dedicated_mask' and " + "'hw:cpu_realtime_mask' can not be specified at the same " + "time, or be specified with none of them.") class MixedInstanceNotSupportByComputeService(NovaException): diff --git a/nova/scheduler/utils.py b/nova/scheduler/utils.py index 251a28aef558..c898fbf86e4d 100644 --- a/nova/scheduler/utils.py +++ b/nova/scheduler/utils.py @@ -280,11 +280,13 @@ class ResourceRequest(object): if cpu_policy == obj_fields.CPUAllocationPolicy.MIXED: # Get dedicated CPU list from flavor extra spec. For a mixed - # instance a non-empty 'hw:cpu_dedicated_mask' configuration must - # exist, which is already ensured in the API layer. + # instance a non-empty 'hw:cpu_dedicated_mask' or realtime CPU + # mask configuration must exist, which is already ensured in + # the API layer. dedicated_cpus = hardware.get_dedicated_cpu_constraint(flavor) + realtime_cpus = hardware.get_realtime_cpu_constraint(flavor, image) - pcpus = len(dedicated_cpus) + pcpus = len(dedicated_cpus or realtime_cpus) vcpus = flavor.vcpus - pcpus # apply for the VCPU resource of a 'mixed' instance diff --git a/nova/tests/unit/scheduler/test_utils.py b/nova/tests/unit/scheduler/test_utils.py index c9cc77980a4f..4c44418088a2 100644 --- a/nova/tests/unit/scheduler/test_utils.py +++ b/nova/tests/unit/scheduler/test_utils.py @@ -1080,17 +1080,10 @@ class TestUtils(TestUtilsBase): self.assertResourceRequestsEqual(expected, rr) self.assertFalse(rr.cpu_pinning_requested) - def test_resource_request_init_with_mixed_cpus(self): - """Ensure the mixed instance properly requests the PCPU, VCPU, - MEMORY_MB, DISK_GB resources. - """ + def _test_resource_request_init_with_mixed_cpus(self, extra_specs): flavor = objects.Flavor( vcpus=4, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0, - extra_specs={ - 'hw:cpu_policy': 'mixed', - 'hw:cpu_dedicated_mask': '2,3' - }) - + extra_specs=extra_specs) rs = objects.RequestSpec(flavor=flavor) expected = FakeResourceRequest() expected._rg_by_id[None] = objects.RequestGroup( @@ -1106,19 +1099,33 @@ class TestUtils(TestUtilsBase): rr = utils.ResourceRequest(rs) self.assertResourceRequestsEqual(expected, rr) - def test_resource_request_init_with_mixed_cpus_isolate_emulator(self): - """Ensure the mixed instance properly requests the PCPU, VCPU, - MEMORY_MB, DISK_GB resources, ensure an extra PCPU resource is - requested due to a ISOLATE emulator thread policy. + def test_resource_request_init_with_mixed_cpus_dedicated(self): + """Ensure the mixed instance, which is generated through + 'hw:cpu_dedicated_mask' extra spec, properly requests the PCPU, VCPU, + MEMORY_MB and DISK_GB resources. """ + extra_specs = { + 'hw:cpu_policy': 'mixed', + 'hw:cpu_dedicated_mask': '2,3' + } + self._test_resource_request_init_with_mixed_cpus(extra_specs) + + def test_resource_request_init_with_mixed_cpus_realtime(self): + """Ensure the mixed instance, which is generated through real-time CPU + interface, properly requests the PCPU, VCPU, MEMORY_BM and DISK_GB + resources. + """ + extra_specs = { + 'hw:cpu_policy': 'mixed', + "hw:cpu_realtime": "yes", + "hw:cpu_realtime_mask": '2,3' + } + self._test_resource_request_init_with_mixed_cpus(extra_specs) + + def _test_resource_request_init_with_mixed_cpus_iso_emu(self, extra_specs): flavor = objects.Flavor( vcpus=4, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0, - extra_specs={ - 'hw:cpu_policy': 'mixed', - 'hw:cpu_dedicated_mask': '2,3', - 'hw:emulator_threads_policy': 'isolate', - }) - + extra_specs=extra_specs) rs = objects.RequestSpec(flavor=flavor) expected = FakeResourceRequest() expected._rg_by_id[None] = objects.RequestGroup( @@ -1136,6 +1143,33 @@ class TestUtils(TestUtilsBase): rr = utils.ResourceRequest(rs) self.assertResourceRequestsEqual(expected, rr) + def test_resource_request_init_with_mixed_cpus_iso_emu_realtime(self): + """Ensure the mixed instance, which is generated through the + 'hw:cpu_dedicated_mask' extra spec, specs, properly requests the PCPU, + VCPU, MEMORY_MB, DISK_GB resources, ensure an extra PCPU resource is + requested due to a ISOLATE emulator thread policy. + """ + extra_specs = { + 'hw:cpu_policy': 'mixed', + 'hw:cpu_dedicated_mask': '2,3', + 'hw:emulator_threads_policy': 'isolate', + } + self._test_resource_request_init_with_mixed_cpus_iso_emu(extra_specs) + + def test_resource_request_init_with_mixed_cpus_iso_emu_dedicated(self): + """Ensure the mixed instance, which is generated through realtime extra + specs, properly requests the PCPU, VCPU, MEMORY_MB, DISK_GB resources, + ensure an extra PCPU resource is requested due to a ISOLATE emulator + thread policy. + """ + extra_specs = { + 'hw:cpu_policy': 'mixed', + "hw:cpu_realtime": "yes", + "hw:cpu_realtime_mask": '2,3', + 'hw:emulator_threads_policy': 'isolate', + } + self._test_resource_request_init_with_mixed_cpus_iso_emu(extra_specs) + def test_resource_request_init_is_bfv(self): flavor = objects.Flavor( vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=1555) diff --git a/nova/tests/unit/virt/test_hardware.py b/nova/tests/unit/virt/test_hardware.py index 6554e6abacfc..5661ff9a6086 100644 --- a/nova/tests/unit/virt/test_hardware.py +++ b/nova/tests/unit/virt/test_hardware.py @@ -1729,6 +1729,37 @@ class NUMATopologyTest(test.NoDBTestCase): ] ), }, + { + "flavor": objects.Flavor(vcpus=6, memory_mb=2048, + extra_specs={ + "hw:cpu_policy": fields.CPUAllocationPolicy.MIXED, + "hw:cpu_realtime": "yes", + "hw:cpu_realtime_mask": "^0-2", + }), + "image": { + "properties": {}, + }, + "expect": objects.InstanceNUMATopology( + cells=[ + objects.InstanceNUMACell( + id=0, cpuset=set([0, 1, 2]), + pcpuset=set([3, 4, 5]), memory=2048, + cpu_policy=fields.CPUAllocationPolicy.MIXED)]), + }, + { # Create 'mixed' instance with the 'ISOLATE' emulator + # thread policy + "flavor": objects.Flavor(vcpus=4, memory_mb=2048, + extra_specs={ + "hw:cpu_policy": "mixed", + "hw:cpu_dedicated_mask": "3", + "hw:cpu_realtime": "yes", + "hw:cpu_realtime_mask": "^0-2" + }), + "image": { + "properties": {} + }, + "expect": exception.RequiredMixedOrRealtimeCPUMask, + }, { # Invalid CPU thread pinning override "flavor": objects.Flavor( diff --git a/nova/virt/hardware.py b/nova/virt/hardware.py index c58acfbff639..de43986b06a9 100644 --- a/nova/virt/hardware.py +++ b/nova/virt/hardware.py @@ -1894,7 +1894,7 @@ def numa_get_constraints(flavor, image_meta): cpu_policy = get_cpu_policy_constraint(flavor, image_meta) cpu_thread_policy = get_cpu_thread_policy_constraint(flavor, image_meta) - rt_mask = get_realtime_cpu_constraint(flavor, image_meta) + realtime_cpus = get_realtime_cpu_constraint(flavor, image_meta) dedicated_cpus = get_dedicated_cpu_constraint(flavor) emu_threads_policy = get_emulator_thread_policy_constraint(flavor) @@ -1966,7 +1966,7 @@ def numa_get_constraints(flavor, image_meta): if dedicated_cpus: raise exception.RequiredMixedInstancePolicy() - if rt_mask: + if realtime_cpus: raise exception.RealtimeConfigurationInvalid() elif cpu_policy == fields.CPUAllocationPolicy.DEDICATED: # 'hw:cpu_dedicated_mask' should not be defined in a flavor with @@ -1974,9 +1974,16 @@ def numa_get_constraints(flavor, image_meta): if dedicated_cpus: raise exception.RequiredMixedInstancePolicy() else: # MIXED - if dedicated_cpus is None: + if realtime_cpus and dedicated_cpus: raise exception.RequiredMixedOrRealtimeCPUMask() + if not (realtime_cpus or dedicated_cpus): + raise exception.RequiredMixedOrRealtimeCPUMask() + + # NOTE(huaquiang): If using mixed with realtime, then cores listed in + # the realtime mask are dedicated and everything else is shared. + dedicated_cpus = dedicated_cpus or realtime_cpus + nodes = _get_numa_node_count_constraint(flavor, image_meta) pagesize = _get_numa_pagesize_constraint(flavor, image_meta) vpmems = get_vpmems(flavor)