Add HPET timer support for x86 guests

This commit adds support for the High Precision Event Timer (HPET) for
x86 guests in the libvirt driver. The timer can be set by image property
'hw_time_hpet'. By default it remains turned off. When it is turned on
the HPET timer is activated in libvirt.

If the image property 'hw_time_hpet' is incorrectly set to a
non-boolean, the HPET timer remains turned off.

blueprint: support-hpet-on-guest
Change-Id: I3debf725544cae245fd31a8d97650392965d480a
Signed-off-by: Jack Ding <jack.ding@windriver.com>
This commit is contained in:
Jack Ding 2018-09-27 12:52:56 -04:00 committed by Matt Riedemann
parent 47bcc39cd6
commit 9e884de68a
5 changed files with 169 additions and 3 deletions

View File

@ -170,12 +170,15 @@ class ImageMetaProps(base.NovaObject):
# Version 1.18: Pull signature properties from cursive library # Version 1.18: Pull signature properties from cursive library
# Version 1.19: Added 'img_hide_hypervisor_id' type field # Version 1.19: Added 'img_hide_hypervisor_id' type field
# Version 1.20: Added 'traits_required' list field # Version 1.20: Added 'traits_required' list field
VERSION = '1.20' # Version 1.21: Added 'hw_time_hpet' field
VERSION = '1.21'
def obj_make_compatible(self, primitive, target_version): def obj_make_compatible(self, primitive, target_version):
super(ImageMetaProps, self).obj_make_compatible(primitive, super(ImageMetaProps, self).obj_make_compatible(primitive,
target_version) target_version)
target_version = versionutils.convert_version_to_tuple(target_version) target_version = versionutils.convert_version_to_tuple(target_version)
if target_version < (1, 21):
primitive.pop('hw_time_hpet', None)
if target_version < (1, 20): if target_version < (1, 20):
primitive.pop('traits_required', None) primitive.pop('traits_required', None)
if target_version < (1, 19): if target_version < (1, 19):
@ -326,6 +329,9 @@ class ImageMetaProps(base.NovaObject):
# name of the RNG device type eg virtio # name of the RNG device type eg virtio
'hw_rng_model': fields.RNGModelField(), 'hw_rng_model': fields.RNGModelField(),
# boolean 'true' or 'false' to enable HPET
'hw_time_hpet': fields.FlexibleBooleanField(),
# number of serial ports to create # number of serial ports to create
'hw_serial_port_count': fields.IntegerField(), 'hw_serial_port_count': fields.IntegerField(),

View File

@ -1093,7 +1093,7 @@ object_data = {
'HVSpec': '1.2-de06bcec472a2f04966b855a49c46b41', 'HVSpec': '1.2-de06bcec472a2f04966b855a49c46b41',
'IDEDeviceBus': '1.0-29d4c9f27ac44197f01b6ac1b7e16502', 'IDEDeviceBus': '1.0-29d4c9f27ac44197f01b6ac1b7e16502',
'ImageMeta': '1.8-642d1b2eb3e880a367f37d72dd76162d', 'ImageMeta': '1.8-642d1b2eb3e880a367f37d72dd76162d',
'ImageMetaProps': '1.20-ffd686cde289814695d5f89522aa5aef', 'ImageMetaProps': '1.21-f3721d8f744a9507a1966c81c386b532',
'Instance': '2.4-4437eb8b2737c3054ea579b8efe31dc5', 'Instance': '2.4-4437eb8b2737c3054ea579b8efe31dc5',
'InstanceAction': '1.1-f9f293e526b66fca0d05c3b3a2d13914', 'InstanceAction': '1.1-f9f293e526b66fca0d05c3b3a2d13914',
'InstanceActionEvent': '1.2-b2f368b8a29d8d872b1f6ea841e820a0', 'InstanceActionEvent': '1.2-b2f368b8a29d8d872b1f6ea841e820a0',

View File

@ -3633,6 +3633,153 @@ class LibvirtConnTestCase(test.NoDBTestCase,
else: else:
self.assertEqual(2, len(cfg.clock.timers)) self.assertEqual(2, len(cfg.clock.timers))
def test_get_guest_config_clock_hpet_false(self):
self.flags(virt_type='kvm', group='libvirt')
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
instance_ref = objects.Instance(**self.test_instance)
image_meta = objects.ImageMeta.from_dict({
"disk_format": "raw",
"properties": {"hw_time_hpet": "false"}})
disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
instance_ref,
image_meta)
hpet_map = {
fields.Architecture.X86_64: True,
fields.Architecture.I686: True,
fields.Architecture.PPC: False,
fields.Architecture.PPC64: False,
fields.Architecture.ARMV7: False,
fields.Architecture.AARCH64: False,
}
for guestarch, expect_hpet in hpet_map.items():
with mock.patch.object(libvirt_driver.libvirt_utils,
'get_arch',
return_value=guestarch):
cfg = drvr._get_guest_config(instance_ref, [],
image_meta,
disk_info)
self.assertIsInstance(cfg.clock,
vconfig.LibvirtConfigGuestClock)
self.assertEqual(cfg.clock.offset, "utc")
self.assertIsInstance(cfg.clock.timers[0],
vconfig.LibvirtConfigGuestTimer)
self.assertIsInstance(cfg.clock.timers[1],
vconfig.LibvirtConfigGuestTimer)
self.assertEqual(cfg.clock.timers[0].name, "pit")
self.assertEqual(cfg.clock.timers[0].tickpolicy,
"delay")
self.assertEqual(cfg.clock.timers[1].name, "rtc")
self.assertEqual(cfg.clock.timers[1].tickpolicy,
"catchup")
if expect_hpet:
self.assertEqual(3, len(cfg.clock.timers))
self.assertIsInstance(cfg.clock.timers[2],
vconfig.LibvirtConfigGuestTimer)
self.assertEqual('hpet', cfg.clock.timers[2].name)
self.assertFalse(cfg.clock.timers[2].present)
else:
self.assertEqual(2, len(cfg.clock.timers))
def test_get_guest_config_clock_hpet_true(self):
self.flags(virt_type='kvm', group='libvirt')
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
instance_ref = objects.Instance(**self.test_instance)
image_meta = objects.ImageMeta.from_dict({
"id": uuids.image_id,
"disk_format": "raw",
"properties": {"hw_time_hpet": "true"}})
disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
instance_ref,
image_meta)
hpet_map = {
fields.Architecture.X86_64: True,
fields.Architecture.I686: True,
fields.Architecture.PPC: False,
fields.Architecture.PPC64: False,
fields.Architecture.ARMV7: False,
fields.Architecture.AARCH64: False,
}
for guestarch, expect_hpet in hpet_map.items():
with mock.patch.object(libvirt_driver.libvirt_utils,
'get_arch',
return_value=guestarch):
cfg = drvr._get_guest_config(instance_ref, [],
image_meta,
disk_info)
self.assertIsInstance(cfg.clock,
vconfig.LibvirtConfigGuestClock)
self.assertEqual(cfg.clock.offset, "utc")
self.assertIsInstance(cfg.clock.timers[0],
vconfig.LibvirtConfigGuestTimer)
self.assertIsInstance(cfg.clock.timers[1],
vconfig.LibvirtConfigGuestTimer)
self.assertEqual(cfg.clock.timers[0].name, "pit")
self.assertEqual(cfg.clock.timers[0].tickpolicy,
"delay")
self.assertEqual(cfg.clock.timers[1].name, "rtc")
self.assertEqual(cfg.clock.timers[1].tickpolicy,
"catchup")
if expect_hpet:
self.assertEqual(3, len(cfg.clock.timers))
self.assertIsInstance(cfg.clock.timers[2],
vconfig.LibvirtConfigGuestTimer)
self.assertEqual('hpet', cfg.clock.timers[2].name)
self.assertTrue(cfg.clock.timers[2].present)
else:
self.assertEqual(2, len(cfg.clock.timers))
def test_get_guest_config_clock_hpet_invalid(self):
self.flags(virt_type='kvm', group='libvirt')
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
instance_ref = objects.Instance(**self.test_instance)
image_meta = objects.ImageMeta.from_dict({
"disk_format": "raw",
"properties": {"hw_time_hpet": "blah"}})
disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
instance_ref,
image_meta)
hpet_map = {
fields.Architecture.X86_64: True,
fields.Architecture.I686: True,
fields.Architecture.PPC: False,
fields.Architecture.PPC64: False,
fields.Architecture.ARMV7: False,
fields.Architecture.AARCH64: False,
}
for guestarch, expect_hpet in hpet_map.items():
with mock.patch.object(libvirt_driver.libvirt_utils,
'get_arch',
return_value=guestarch):
cfg = drvr._get_guest_config(instance_ref, [],
image_meta,
disk_info)
self.assertIsInstance(cfg.clock,
vconfig.LibvirtConfigGuestClock)
self.assertEqual(cfg.clock.offset, "utc")
self.assertIsInstance(cfg.clock.timers[0],
vconfig.LibvirtConfigGuestTimer)
self.assertIsInstance(cfg.clock.timers[1],
vconfig.LibvirtConfigGuestTimer)
self.assertEqual(cfg.clock.timers[0].name, "pit")
self.assertEqual(cfg.clock.timers[0].tickpolicy,
"delay")
self.assertEqual(cfg.clock.timers[1].name, "rtc")
self.assertEqual(cfg.clock.timers[1].tickpolicy,
"catchup")
if expect_hpet:
self.assertEqual(3, len(cfg.clock.timers))
self.assertIsInstance(cfg.clock.timers[2],
vconfig.LibvirtConfigGuestTimer)
self.assertEqual('hpet', cfg.clock.timers[2].name)
# a non-boolean value of hw_time_hpet should be treated as
# False
self.assertFalse(cfg.clock.timers[2].present)
else:
self.assertEqual(2, len(cfg.clock.timers))
@mock.patch.object(libvirt_utils, 'get_arch') @mock.patch.object(libvirt_utils, 'get_arch')
def test_get_guest_config_windows_timer(self, mock_get_arch): def test_get_guest_config_windows_timer(self, mock_get_arch):
mock_get_arch.return_value = fields.Architecture.I686 mock_get_arch.return_value = fields.Architecture.I686

View File

@ -4569,6 +4569,7 @@ class LibvirtDriver(driver.ComputeDriver):
clk.add_timer(tmpit) clk.add_timer(tmpit)
clk.add_timer(tmrtc) clk.add_timer(tmrtc)
hpet = image_meta.properties.get('hw_time_hpet', False)
guestarch = libvirt_utils.get_arch(image_meta) guestarch = libvirt_utils.get_arch(image_meta)
if guestarch in (fields.Architecture.I686, if guestarch in (fields.Architecture.I686,
fields.Architecture.X86_64): fields.Architecture.X86_64):
@ -4576,8 +4577,12 @@ class LibvirtDriver(driver.ComputeDriver):
# qemu -no-hpet is not supported on non-x86 targets. # qemu -no-hpet is not supported on non-x86 targets.
tmhpet = vconfig.LibvirtConfigGuestTimer() tmhpet = vconfig.LibvirtConfigGuestTimer()
tmhpet.name = "hpet" tmhpet.name = "hpet"
tmhpet.present = False tmhpet.present = hpet
clk.add_timer(tmhpet) clk.add_timer(tmhpet)
else:
if hpet:
LOG.warning('HPET is not turned on for non-x86 guests in image'
' %s.', image_meta.id)
# Provide Windows guests with the paravirtualized hyperv timer source. # Provide Windows guests with the paravirtualized hyperv timer source.
# This is the windows equiv of kvm-clock, allowing Windows # This is the windows equiv of kvm-clock, allowing Windows

View File

@ -0,0 +1,8 @@
---
features:
- |
Added support for the High Precision Event Timer (HPET) for x86 guests
in the libvirt driver when image property ``hypervisor_type=qemu`` is set.
The timer can be set by setting a ``hw_time_hpet=True`` image property
key/value pair. By default HPET remains turned off. When it is turned on
the HPET is activated in libvirt.