diff --git a/nova/tests/unit/virt/test_hardware.py b/nova/tests/unit/virt/test_hardware.py index 45d5ed13b3..62214f8d21 100644 --- a/nova/tests/unit/virt/test_hardware.py +++ b/nova/tests/unit/virt/test_hardware.py @@ -4050,6 +4050,131 @@ class CPUPinningTestCase(test.NoDBTestCase, _CPUPinningTestCaseBase): self.assertEqual(set([3]), new_cell.cells[0].pinned_cpus) self.assertEqual(0, new_cell.cells[0].cpu_usage) + def test_host_usage_from_mixed_instance(self): + """Ensure pinned and unpinned CPUs are correctly consumed for a mixed + instance without an emulator thread policy. + """ + host_topo = objects.NUMATopology(cells=[ + objects.NUMACell( + id=0, + cpuset=set([0, 1, 4, 5]), + pcpuset=set([2, 3, 6, 7]), + memory=4096, + cpu_usage=0, + memory_usage=0, + pinned_cpus=set([2]), + siblings=[set([0, 4]), set([1, 5]), set([2, 6]), set([3, 7])], + mempages=[objects.NUMAPagesTopology( + size_kb=4, total=524288, used=0)] + ), + ]) + inst_topo = objects.InstanceNUMATopology(cells=[ + objects.InstanceNUMACell( + cpuset=set([0, 1]), pcpuset=set([2, 3]), memory=2048, + id=0, cpu_pinning={2: 3, 3: 6}, + cpu_policy=fields.CPUAllocationPolicy.MIXED + ), + ]) + + new_cell = hw.numa_usage_from_instance_numa(host_topo, inst_topo) + self.assertEqual({2, 3, 6}, new_cell.cells[0].pinned_cpus) + self.assertEqual(2, new_cell.cells[0].cpu_usage) + + def test_host_usage_from_mixed_instance_free(self): + """Ensure pinned and unpinned CPUs are correctly freed for a mixed + instance without an emulator thread policy. + """ + host_topo = objects.NUMATopology(cells=[ + objects.NUMACell( + id=0, + cpuset=set([0, 1, 4, 5]), + pcpuset=set([2, 3, 6, 7]), + memory=4096, + cpu_usage=2, + memory_usage=0, + pinned_cpus=set([2, 6, 7]), + siblings=[set([0, 4]), set([1, 5]), set([2, 6]), set([3, 7])], + mempages=[objects.NUMAPagesTopology( + size_kb=4, total=524288, used=0)] + ), + ]) + inst_topo = objects.InstanceNUMATopology(cells=[ + objects.InstanceNUMACell( + cpuset=set([0, 1]), pcpuset=set([2, 3]), memory=2048, + id=0, cpu_pinning={2: 6, 3: 7}, + cpu_policy=fields.CPUAllocationPolicy.MIXED + ), + ]) + + new_cell = hw.numa_usage_from_instance_numa(host_topo, inst_topo, + free=True) + self.assertEqual({2}, new_cell.cells[0].pinned_cpus) + self.assertEqual(0, new_cell.cells[0].cpu_usage) + + def test_host_usage_from_mixed_instance_isolate(self): + """Ensure pinned and unpinned CPUs are correctly consumed for a mixed + instance with an ISOLATE emulator thread policy. + """ + host_topo = objects.NUMATopology(cells=[ + objects.NUMACell( + id=0, + cpuset=set([2, 3, 6, 7]), + pcpuset=set([0, 1, 4, 5]), + memory=4096, + cpu_usage=2, + memory_usage=0, + pinned_cpus=set(), + siblings=[set([0, 4]), set([1, 5]), set([2, 6]), set([3, 7])], + mempages=[objects.NUMAPagesTopology( + size_kb=4, total=524288, used=0)] + ), + ]) + inst_topo = objects.InstanceNUMATopology(cells=[ + objects.InstanceNUMACell( + cpuset=set([0, 1]), pcpuset=set([2, 3]), + memory=2048, id=0, cpu_pinning={2: 0, 3: 1}, + cpu_policy=fields.CPUAllocationPolicy.MIXED, + cpu_thread_policy=fields.CPUThreadAllocationPolicy.ISOLATE + ), + ]) + + new_cell = hw.numa_usage_from_instance_numa(host_topo, inst_topo) + self.assertEqual({0, 1, 4, 5}, new_cell.cells[0].pinned_cpus) + self.assertEqual(4, new_cell.cells[0].cpu_usage) + + def test_host_usage_from_mixed_instance_isolate_free(self): + """Ensure pinned and unpinned CPUs are correctly freed for a mixed + instance with an ISOLATE emulator thread policy. + """ + host_topo = objects.NUMATopology(cells=[ + objects.NUMACell( + id=0, + cpuset=set([2, 3, 6, 7]), + pcpuset=set([0, 1, 4, 5]), + memory=4096, + cpu_usage=2, + memory_usage=0, + pinned_cpus=set([0, 1, 4, 5]), + siblings=[set([0, 4]), set([1, 5]), set([2, 6]), set([3, 7])], + mempages=[objects.NUMAPagesTopology( + size_kb=4, total=524288, used=0)] + ), + ]) + inst_topo = objects.InstanceNUMATopology(cells=[ + objects.InstanceNUMACell( + cpuset=set([2, 3]), pcpuset=set([0, 1]), + memory=2048, id=0, + cpu_pinning={0: 0, 1: 1}, + cpu_policy=fields.CPUAllocationPolicy.MIXED, + cpu_thread_policy=fields.CPUThreadAllocationPolicy.ISOLATE + ), + ]) + + new_cell = hw.numa_usage_from_instance_numa(host_topo, inst_topo, + free=True) + self.assertEqual(set(), new_cell.cells[0].pinned_cpus) + self.assertEqual(0, new_cell.cells[0].cpu_usage) + class CPUSReservedCellTestCase(test.NoDBTestCase): def _test_reserved(self, reserved): diff --git a/nova/virt/hardware.py b/nova/virt/hardware.py index f4af6faae6..bbdd758b2f 100644 --- a/nova/virt/hardware.py +++ b/nova/virt/hardware.py @@ -1083,7 +1083,10 @@ def _numa_fit_instance_cell( # NOTE(stephenfin): As with memory, do not allow an instance to overcommit # against itself on any NUMA cell - if instance_cell.cpu_policy == fields.CPUAllocationPolicy.DEDICATED: + if instance_cell.cpu_policy in ( + fields.CPUAllocationPolicy.DEDICATED, + fields.CPUAllocationPolicy.MIXED, + ): required_cpus = len(instance_cell.pcpuset) + cpuset_reserved if required_cpus > len(host_cell.pcpuset): LOG.debug('Not enough host cell CPUs to fit instance cell; ' @@ -1104,7 +1107,10 @@ def _numa_fit_instance_cell( }) return None - if instance_cell.cpu_policy == fields.CPUAllocationPolicy.DEDICATED: + if instance_cell.cpu_policy in ( + fields.CPUAllocationPolicy.DEDICATED, + fields.CPUAllocationPolicy.MIXED, + ): LOG.debug('Pinning has been requested') required_cpus = len(instance_cell.pcpuset) + cpuset_reserved if required_cpus > host_cell.avail_pcpus: @@ -2310,10 +2316,11 @@ def numa_usage_from_instance_numa(host_topology, instance_topology, memory_usage = memory_usage + sign * instance_cell.memory - if instance_cell.cpu_policy != ( - fields.CPUAllocationPolicy.DEDICATED + shared_cpus_usage += sign * len(instance_cell.cpuset) + + if instance_cell.cpu_policy in ( + None, fields.CPUAllocationPolicy.SHARED, ): - shared_cpus_usage += sign * len(instance_cell.cpuset) continue pinned_cpus = set(instance_cell.cpu_pinning.values())