Support Cpu Compararion on Aarch64 Platform

The general use case of libvirt's compareCPU() API is to allow comparing
a guest CPU to a host CPU. However, for AArch64, the _only_ use case
libvirt supports is to allow comparing host CPU with a destination CPU
which is accomplished with the help of this patch[1].

So what we're talking about here, in context of AArch64, is comparing
source _host_ CPU to destination _host_ CPU, so as to prevent a guest
from migrating to a completely different host. For AArch64, in the
past we skipped[2] _guest_ CPU to host CPU comparison (the
"traditional" use case) check for AArch64 for two main reasons, and
these reasons are still valid:
1. There are lots of vendors making different AArch64 CPUs, and they
   are all not easily comparable because they all differ in various
   ways.
2. QEMU AArch64 developers themselves recommend that using 'host-
   passthrough' is  the way to run KVM guests on AArch64.

In sum, for AArch64, (a) guests should use 'host-passthrough';
and (b) src host to dest host CPU comparison only makes sense to
know about _host_ CPU compatibility, but you will not get any CPU
config that you can give to a guest. For more detail discussion,
please check link [3] and discussion [4].

[1] https://listman.redhat.com/archives/libvir-list/2020-September/msg01391.html
    — Modify virCPUarmCompare to perform compare actions
[2] https://review.opendev.org/c/openstack/nova/+/589769/1/nova/virt/libvirt/driver.py#6892
[3] https://www.redhat.com/archives/libvir-list/2020-September/msg00262.html
[4] https://www.redhat.com/archives/libvir-list/2020-September/msg00328.html,

Change-Id: I9026f2ec39660408e4ac66fc0a85223e4b25e4d1
Signed-off-by: Kevin Zhao <kevin.zhao@linaro.org>
This commit is contained in:
Kevin Zhao 2020-11-23 22:10:42 -05:00
parent 370830e944
commit a6ef502aa0
4 changed files with 78 additions and 18 deletions

View File

@ -11020,22 +11020,49 @@ class LibvirtConnTestCase(test.NoDBTestCase,
instance)
self.assertIsNone(ret)
@mock.patch.object(host.Host, 'compare_cpu')
@mock.patch.object(fakelibvirt.Connection, 'getLibVersion',
return_value=versionutils.convert_version_to_int(
libvirt_driver.MIN_LIBVIRT_AARCH64_CPU_COMPARE) - 1
)
@mock.patch.object(nova.virt.libvirt, 'config')
def test_compare_cpu_aarch64_skip_comparison(self,
mock_vconfig,
mock_compare):
mock_get_libversion):
instance = objects.Instance(**self.test_instance)
skip_comparison_exc = fakelibvirt.make_libvirtError(
fakelibvirt.libvirtError,
'Host CPU compatibility check does not make '
'sense on AArch64; skip CPU comparison')
mock_compare.side_effect = skip_comparison_exc
self.mock_uname.return_value = fakelibvirt.os_uname(
'Linux', '', '5.4.0-0-generic', '', fields.Architecture.AARCH64)
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
ret = conn._compare_cpu(None, jsonutils.dumps(_fake_cpu_info_aarch64),
instance)
self.assertIsNone(ret)
@mock.patch.object(host.Host, 'get_capabilities')
@mock.patch.object(fakelibvirt.Connection, 'getLibVersion',
return_value=versionutils.convert_version_to_int(
libvirt_driver.MIN_LIBVIRT_AARCH64_CPU_COMPARE))
@mock.patch.object(host.Host, 'compare_cpu')
def test_compare_cpu_host_aarch64(self,
mock_compare,
mock_get_libversion,
mock_caps):
instance = objects.Instance(**self.test_instance)
mock_compare.return_value = 6
caps = vconfig.LibvirtConfigCaps()
caps.host = vconfig.LibvirtConfigCapsHost()
caps.host.cpu = vconfig.LibvirtConfigCPU()
caps.host.cpu.arch = fields.Architecture.AARCH64
caps.host.topology = fakelibvirt.NUMATopology()
mock_caps.return_value = caps
self.mock_uname.return_value = fakelibvirt.os_uname(
'Linux', '', '5.4.0-0-generic', '', fields.Architecture.AARCH64)
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
ret = conn._compare_cpu(None,
jsonutils.dumps(_fake_cpu_info_aarch64),
instance)
mock_compare.assert_called_once_with(caps.host.cpu.to_xml())
self.assertIsNone(ret)
@mock.patch.object(host.Host, 'compare_cpu')
@mock.patch.object(nova.virt.libvirt.LibvirtDriver,
'_vcpu_model_to_cpu_config')

View File

@ -689,6 +689,23 @@ class HostTestCase(test.NoDBTestCase):
self.assertIsNone(caps.host.cpu.model)
self.assertEqual(0, len(caps.host.cpu.features))
def test_get_capabilities_on_aarch64(self):
"""Tests that cpu features are not retrieved on aarch64 platform.
"""
fake_caps_xml = '''
<capabilities>
<host>
<uuid>cef19ce0-0ca2-11df-855d-b193fbce7686</uuid>
<cpu>
<arch>aarch64</arch>
</cpu>
</host>
</capabilities>'''
with mock.patch.object(fakelibvirt.virConnect, 'getCapabilities',
return_value=fake_caps_xml):
caps = self.host.get_capabilities()
self.assertEqual(0, len(caps.host.cpu.features))
def test__get_machine_types(self):
expected = [
# NOTE(aspiers): in the real world, i686 would probably

View File

@ -219,6 +219,8 @@ MIN_QEMU_VERSION = (4, 2, 0)
NEXT_MIN_LIBVIRT_VERSION = (7, 0, 0)
NEXT_MIN_QEMU_VERSION = (5, 2, 0)
MIN_LIBVIRT_AARCH64_CPU_COMPARE = (6, 9, 0)
# Virtuozzo driver support
MIN_VIRTUOZZO_VERSION = (7, 0, 0)
@ -9350,6 +9352,20 @@ class LibvirtDriver(driver.ComputeDriver):
else:
cpu = self._vcpu_model_to_cpu_config(guest_cpu)
host_cpu = self._host.get_capabilities().host.cpu
if host_cpu.arch == fields.Architecture.AARCH64:
if self._host.has_min_version(MIN_LIBVIRT_AARCH64_CPU_COMPARE):
LOG.debug("On AArch64 hosts, source and destination host "
"CPUs are compared to check if they're compatible"
"(the only use-case supported by libvirt for "
"Arm64/AArch64)")
cpu = host_cpu
else:
LOG.debug("You need %s libvirt version to be able to compare "
"source host CPU with destination host CPU; skip "
"CPU comparison", MIN_LIBVIRT_AARCH64_CPU_COMPARE)
return
u = ("http://libvirt.org/html/libvirt-libvirt-host.html#"
"virCPUCompareResult")
m = _("CPU doesn't have compatibility.\n\n%(ret)s\n\nRefer to %(u)s")
@ -9359,10 +9375,6 @@ class LibvirtDriver(driver.ComputeDriver):
LOG.debug("cpu compare xml: %s", cpu_xml, instance=instance)
ret = self._host.compare_cpu(cpu_xml)
except libvirt.libvirtError as e:
if cpu.arch == fields.Architecture.AARCH64:
LOG.debug("Host CPU compatibility check does not make "
"sense on AArch64; skip CPU comparison")
return
error_code = e.get_error_code()
if error_code == libvirt.VIR_ERR_NO_SUPPORT:
LOG.debug("URI %(uri)s does not support cpu comparison. "

View File

@ -793,13 +793,17 @@ class Host(object):
xml_str = self._caps.host.cpu.to_xml()
if isinstance(xml_str, bytes):
xml_str = xml_str.decode('utf-8')
features = self.get_connection().baselineCPU(
[xml_str],
libvirt.VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES)
if features:
cpu = vconfig.LibvirtConfigCPU()
cpu.parse_str(features)
self._caps.host.cpu.features = cpu.features
# NOTE(kevinz): The baseline CPU info on Aarch64 will not
# include any features. So on Aarch64, we use the original
# features from LibvirtConfigCaps.
if self._caps.host.cpu.arch != fields.Architecture.AARCH64:
features = self.get_connection().baselineCPU(
[xml_str],
libvirt.VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES)
if features:
cpu = vconfig.LibvirtConfigCPU()
cpu.parse_str(features)
self._caps.host.cpu.features = cpu.features
except libvirt.libvirtError as ex:
error_code = ex.get_error_code()
if error_code == libvirt.VIR_ERR_NO_SUPPORT: