diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 5ddcd693d854..b1f79397779a 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -22918,11 +22918,21 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin): @mock.patch.object(libvirt_driver.LibvirtDriver, '_get_cpu_feature_traits', new=mock.Mock(return_value={})) - def test_cpu_traits_sev_no_feature_traits(self): + def test_cpu_traits__sev_support(self): for support in (False, True): self.drvr._host._supports_amd_sev = support - self.assertEqual({ot.HW_CPU_X86_AMD_SEV: support}, - self.drvr._get_cpu_traits()) + traits = self.drvr._get_cpu_traits() + self.assertIn(ot.HW_CPU_X86_AMD_SEV, traits) + self.assertEqual(support, traits[ot.HW_CPU_X86_AMD_SEV]) + + @mock.patch.object(libvirt_driver.LibvirtDriver, '_get_cpu_feature_traits', + new=mock.Mock(return_value={})) + def test_cpu_traits__hyperthreading_support(self): + for support in (False, True): + self.drvr._host._has_hyperthreading = support + traits = self.drvr._get_cpu_traits() + self.assertIn(ot.HW_CPU_HYPERTHREADING, traits) + self.assertEqual(support, traits[ot.HW_CPU_HYPERTHREADING]) def test_cpu_traits_with_passthrough_mode(self): """Test getting CPU traits when cpu_mmode is 'host-passthrough', traits diff --git a/nova/tests/unit/virt/libvirt/test_host.py b/nova/tests/unit/virt/libvirt/test_host.py index 8ec7af1c99a1..a0e78830b68d 100644 --- a/nova/tests/unit/virt/libvirt/test_host.py +++ b/nova/tests/unit/virt/libvirt/test_host.py @@ -1166,6 +1166,56 @@ cg /cgroup/memory cg opt1,opt2 0 0 def test_is_cpu_control_policy_capable_ioerror(self, mock_open): self.assertFalse(self.host.is_cpu_control_policy_capable()) + @mock.patch('nova.virt.libvirt.host.libvirt.Connection.getCapabilities') + def test_has_hyperthreading__true(self, mock_cap): + mock_cap.return_value = """ + + + 1f71d34a-7c89-45cf-95ce-3df20fc6b936 + + + + + + + + + + + + + + + + + """ + self.assertTrue(self.host.has_hyperthreading) + + @mock.patch('nova.virt.libvirt.host.libvirt.Connection.getCapabilities') + def test_has_hyperthreading__false(self, mock_cap): + mock_cap.return_value = """ + + + 1f71d34a-7c89-45cf-95ce-3df20fc6b936 + + + + + + + + + + + + + + + + + """ + self.assertFalse(self.host.has_hyperthreading) + vc = fakelibvirt.virConnect diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index ed31c5c024ad..dc8d7f127d6e 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -357,8 +357,8 @@ class LibvirtDriver(driver.ComputeDriver): self._initiator = None self._fc_wwnns = None self._fc_wwpns = None - self._caps = None self._supported_perf_events = [] + self.__has_hyperthreading = None self.firewall_driver = firewall.load_driver( DEFAULT_FIREWALL_DRIVER, host=self._host) @@ -10269,6 +10269,8 @@ class LibvirtDriver(driver.ComputeDriver): """ traits = self._get_cpu_feature_traits() traits[ot.HW_CPU_X86_AMD_SEV] = self._host.supports_amd_sev + traits[ot.HW_CPU_HYPERTHREADING] = self._host.has_hyperthreading + return traits def _get_cpu_feature_traits(self): diff --git a/nova/virt/libvirt/host.py b/nova/virt/libvirt/host.py index a65da0ca9ef2..71d2d3e6d498 100644 --- a/nova/virt/libvirt/host.py +++ b/nova/virt/libvirt/host.py @@ -119,6 +119,8 @@ class Host(object): # memoized by the supports_amd_sev property below. self._supports_amd_sev = None + self._has_hyperthreading = None + @staticmethod def _get_libvirt_proxy_classes(libvirt_module): """Return a tuple for tpool.Proxy's autowrap argument containing all @@ -1188,6 +1190,26 @@ class Host(object): except IOError: return False + @property + def has_hyperthreading(self): + """Determine if host CPU has SMT, a.k.a. HyperThreading. + + :return: True if the host has SMT enabled, else False. + """ + if self._has_hyperthreading is not None: + return self._has_hyperthreading + + self._has_hyperthreading = False + + # we don't use '/capabilities/host/cpu/topology' since libvirt doesn't + # guarantee the accuracy of this information + for cell in self.get_capabilities().host.topology.cells: + if any(len(cpu.siblings) > 1 for cpu in cell.cpus if cpu.siblings): + self._has_hyperthreading = True + break + + return self._has_hyperthreading + def _kernel_supports_amd_sev(self): if not os.path.exists(SEV_KERNEL_PARAM_FILE): LOG.debug("%s does not exist", SEV_KERNEL_PARAM_FILE)