Fix for bug 2140537

If a guest has pinned CPUs the domain XML's
<iothreadpin> should have iothread attribute also.

Closes-Bug: #2140537
Change-Id: I5c2df747a3fdfbd2ee31d50a3d716a0ccc787e15
Signed-off-by: lajoskatona <lajos.katona@est.tech>
This commit is contained in:
lajoskatona
2026-02-05 19:26:54 +01:00
committed by Lajos Katona
parent 76d796193c
commit 873aee5e95
6 changed files with 175 additions and 159 deletions
+8 -21
View File
@@ -1233,6 +1233,14 @@ class Domain(object):
definition['iothreads'] = iothreads.text definition['iothreads'] = iothreads.text
iothread_pin = tree.find('./cputune/iothreadpin') iothread_pin = tree.find('./cputune/iothreadpin')
if iothread_pin is not None and iothread_pin.get('iothread') is None:
raise make_libvirtError(
libvirtError,
"XML error: Missing required attribute 'iothread' "
"in element 'iothreadpin'",
error_code=VIR_ERR_XML_ERROR,
error_domain=VIR_FROM_DOMAIN
)
if iothread_pin is not None: if iothread_pin is not None:
definition['iothread_pin'] = iothread_pin.get('cpuset') definition['iothread_pin'] = iothread_pin.get('cpuset')
@@ -2095,27 +2103,6 @@ class Connection(object):
self._emit_lifecycle(dom, VIR_DOMAIN_EVENT_DEFINED, 0) self._emit_lifecycle(dom, VIR_DOMAIN_EVENT_DEFINED, 0)
return dom return dom
# TODO(lajoskatona): Move this validation to defineXML once fix for
# bug/2140537 is merged.
# This method is only used temporarily from
# nova/tests/functional/regressions/test_bug_2140537.py
def _defineXMLIOThreads(self, xml):
xml_doc = etree.fromstring(xml.encode('utf-8'))
iothreadpin = xml_doc.find('./cputune/iothreadpin')
if iothreadpin is not None and iothreadpin.get('iothread') is None:
raise make_libvirtError(
libvirtError,
"XML error: Missing required attribute 'iothread' "
"in element 'iothreadpin'",
error_code=VIR_ERR_XML_ERROR,
error_domain=VIR_FROM_DOMAIN)
dom = Domain(connection=self, running=False, transient=False, xml=xml)
self._vms[dom.name()] = dom
self._emit_lifecycle(dom, VIR_DOMAIN_EVENT_DEFINED, 0)
return dom
def createXML(self, xml, flags): def createXML(self, xml, flags):
dom = Domain(connection=self, running=True, transient=True, xml=xml) dom = Domain(connection=self, running=True, transient=True, xml=xml)
self._vms[dom.name()] = dom self._vms[dom.name()] = dom
@@ -22,7 +22,6 @@ in element 'iothreadpin'".
""" """
from lxml import etree from lxml import etree
from unittest import mock
from nova.tests.fixtures import libvirt as fakelibvirt from nova.tests.fixtures import libvirt as fakelibvirt
from nova.tests.functional import integrated_helpers from nova.tests.functional import integrated_helpers
@@ -46,15 +45,6 @@ class TestIOThreadPinningPinnedCPU(
ADDITIONAL_FILTERS = ['NUMATopologyFilter'] ADDITIONAL_FILTERS = ['NUMATopologyFilter']
def setUp(self): def setUp(self):
# TODO(lajoskatona): remove this patch when the fix for
# bug/2140537 is merged, and the libvirt fixture has the
# necessary validation for XML fields for IOThreads.
patcher = mock.patch.object(
fakelibvirt.Connection, 'defineXML',
fakelibvirt.Connection._defineXMLIOThreads)
patcher.start()
self.addCleanup(patcher.stop)
super().setUp() super().setUp()
self.hostname = self.start_compute( self.hostname = self.start_compute(
hostname='host1', hostname='host1',
@@ -91,20 +81,34 @@ class TestIOThreadPinningPinnedCPU(
# BUG: This fails with libvirt error about missing 'iothread' attribute # BUG: This fails with libvirt error about missing 'iothread' attribute
# The server creation will fail and go to ERROR state # The server creation will fail and go to ERROR state
# server = self._create_server(
# flavor_id=flavor, host='host1', networks='none',
# expected_state='ERROR')
server = self._create_server( server = self._create_server(
flavor_id=flavor, host='host1', networks='none', flavor_id=flavor, host='host1', networks='none',
expected_state='ERROR') expected_state='ACTIVE')
# Verify the server is in ERROR state due to the libvirt XML error # Verify the server is in ERROR state due to the libvirt XML error
self.assertEqual('ERROR', server['status']) # self.assertEqual('ERROR', server['status'])
# Check the fault message contains the libvirt error # Check the fault message contains the libvirt error
self.assertIn('Exceeded maximum number of retries', # self.assertIn('Exceeded maximum number of retries',
server['fault']['message']) # server['fault']['message'])
# Check the logs for the exception # Check the logs for the exception
self.assertIn("Missing required attribute 'iothread'", # self.assertIn("Missing required attribute 'iothread'",
self.stdlog.logger.output) # self.stdlog.logger.output)
self.assertIn("element 'iothreadpin'", self.stdlog.logger.output) # self.assertIn("element 'iothreadpin'", self.stdlog.logger.output)
# Get source XML and verify pinning matches cpu_shared_set
conn = self.host.driver._host.get_connection()
dom = conn.lookupByUUIDString(server['id'])
srv_xml = dom.XMLDesc(0)
srv_emulatorpin = self._get_xml_element(
srv_xml, './cputune/emulatorpin')
srv_iothreadpin = self._get_xml_element(
srv_xml, './cputune/iothreadpin')
self.assertIsNotNone(srv_emulatorpin)
self.assertIsNotNone(srv_iothreadpin)
def test_iothread_pinning_explicit_numa(self): def test_iothread_pinning_explicit_numa(self):
"""Test iothread pinning with explicit multi-node NUMA topology.""" """Test iothread pinning with explicit multi-node NUMA topology."""
@@ -120,46 +124,46 @@ class TestIOThreadPinningPinnedCPU(
flavor = self._create_flavor(vcpu=4, extra_spec=extra_spec) flavor = self._create_flavor(vcpu=4, extra_spec=extra_spec)
# Server should go ACTIVE # Server should go ACTIVE
# server = self._create_server(
# flavor_id=flavor, host='host1', networks='none',
# expected_state='ACTIVE')
server = self._create_server( server = self._create_server(
flavor_id=flavor, host='host1', networks='none', flavor_id=flavor, host='host1', networks='none',
expected_state='ERROR') expected_state='ACTIVE')
# server = self._create_server(
# flavor_id=flavor, host='host1', networks='none',
# expected_state='ERROR')
# conn = self.host.driver._host.get_connection() conn = self.host.driver._host.get_connection()
# dom = conn.lookupByUUIDString(server['id']) dom = conn.lookupByUUIDString(server['id'])
# srv_xml = dom.XMLDesc(0) srv_xml = dom.XMLDesc(0)
# # Should have iothreads element # Should have iothreads element
# srv_iothread = self._get_xml_element(srv_xml, './iothreads') srv_iothread = self._get_xml_element(srv_xml, './iothreads')
# self.assertIsNotNone(srv_iothread) self.assertIsNotNone(srv_iothread)
# self.assertEqual('1', srv_iothread.text) self.assertEqual('1', srv_iothread.text)
# # Should have emulatorpin and iothreadpin # Should have emulatorpin and iothreadpin
# srv_emulatorpin = self._get_xml_element( srv_emulatorpin = self._get_xml_element(
# srv_xml, './cputune/emulatorpin') srv_xml, './cputune/emulatorpin')
# srv_iothreadpin = self._get_xml_element( srv_iothreadpin = self._get_xml_element(
# srv_xml, './cputune/iothreadpin') srv_xml, './cputune/iothreadpin')
# self.assertIsNotNone(srv_emulatorpin) self.assertIsNotNone(srv_emulatorpin)
# self.assertIsNotNone(srv_iothreadpin) self.assertIsNotNone(srv_iothreadpin)
# # iothreadpin should have iothread attribute set to 1 # iothreadpin should have iothread attribute set to 1
# self.assertEqual('1', srv_iothreadpin.get('iothread')) self.assertEqual('1', srv_iothreadpin.get('iothread'))
# # Both should be pinned to the union of NUMA nodes # Both should be pinned to the union of NUMA nodes
# self.assertEqual(srv_emulatorpin.get('cpuset'), self.assertEqual(srv_emulatorpin.get('cpuset'),
# srv_iothreadpin.get('cpuset')) srv_iothreadpin.get('cpuset'))
self.assertEqual('ERROR', server['status']) # self.assertEqual('ERROR', server['status'])
# Check the fault message contains the libvirt error # # Check the fault message contains the libvirt error
self.assertIn('Exceeded maximum number of retries', # self.assertIn('Exceeded maximum number of retries',
server['fault']['message']) # server['fault']['message'])
# Check the logs for the exception # # Check the logs for the exception
self.assertIn("Missing required attribute 'iothread'", # self.assertIn("Missing required attribute 'iothread'",
self.stdlog.logger.output) # self.stdlog.logger.output)
self.assertIn("element 'iothreadpin'", self.stdlog.logger.output) # self.assertIn("element 'iothreadpin'", self.stdlog.logger.output)
def test_iothread_pinning_isolated_emulator(self): def test_iothread_pinning_isolated_emulator(self):
"""Test iothread pinning with isolated emulator threads policy.""" """Test iothread pinning with isolated emulator threads policy."""
@@ -174,52 +178,52 @@ class TestIOThreadPinningPinnedCPU(
} }
flavor = self._create_flavor(vcpu=2, extra_spec=extra_spec) flavor = self._create_flavor(vcpu=2, extra_spec=extra_spec)
server = self._create_server(
flavor_id=flavor, host='host1', networks='none',
expected_state='ERROR')
# Server should go ACTIVE
# server = self._create_server( # server = self._create_server(
# flavor_id=flavor, host='host1', networks='none', # flavor_id=flavor, host='host1', networks='none',
# expected_state='ACTIVE') # expected_state='ERROR')
# Server should go ACTIVE
server = self._create_server(
flavor_id=flavor, host='host1', networks='none',
expected_state='ACTIVE')
# conn = self.host.driver._host.get_connection() conn = self.host.driver._host.get_connection()
# dom = conn.lookupByUUIDString(server['id']) dom = conn.lookupByUUIDString(server['id'])
# srv_xml = dom.XMLDesc(0) srv_xml = dom.XMLDesc(0)
# # Should have iothreads element # Should have iothreads element
# srv_iothread = self._get_xml_element(srv_xml, './iothreads') srv_iothread = self._get_xml_element(srv_xml, './iothreads')
# self.assertIsNotNone(srv_iothread) self.assertIsNotNone(srv_iothread)
# self.assertEqual('1', srv_iothread.text) self.assertEqual('1', srv_iothread.text)
# # Should have emulatorpin and iothreadpin # Should have emulatorpin and iothreadpin
# srv_emulatorpin = self._get_xml_element( srv_emulatorpin = self._get_xml_element(
# srv_xml, './cputune/emulatorpin') srv_xml, './cputune/emulatorpin')
# srv_iothreadpin = self._get_xml_element( srv_iothreadpin = self._get_xml_element(
# srv_xml, './cputune/iothreadpin') srv_xml, './cputune/iothreadpin')
# self.assertIsNotNone(srv_emulatorpin) self.assertIsNotNone(srv_emulatorpin)
# self.assertIsNotNone(srv_iothreadpin) self.assertIsNotNone(srv_iothreadpin)
# # iothreadpin should have iothread attribute set to 1 # iothreadpin should have iothread attribute set to 1
# self.assertEqual('1', srv_iothreadpin.get('iothread')) self.assertEqual('1', srv_iothreadpin.get('iothread'))
# # Both should be pinned to the same reserved/isolated CPU # Both should be pinned to the same reserved/isolated CPU
# self.assertEqual(srv_emulatorpin.get('cpuset'), self.assertEqual(srv_emulatorpin.get('cpuset'),
# srv_iothreadpin.get('cpuset')) srv_iothreadpin.get('cpuset'))
# # Should be pinned to a single CPU (the reserved one) # Should be pinned to a single CPU (the reserved one)
# # With vcpu=2 and isolate policy, one extra CPU is reserved # With vcpu=2 and isolate policy, one extra CPU is reserved
# cpuset = srv_iothreadpin.get('cpuset') cpuset = srv_iothreadpin.get('cpuset')
# # The cpuset should be a single CPU from cpu_dedicated_set # The cpuset should be a single CPU from cpu_dedicated_set
# self.assertIsNotNone(cpuset) self.assertIsNotNone(cpuset)
self.assertEqual('ERROR', server['status']) # self.assertEqual('ERROR', server['status'])
# Check the fault message contains the libvirt error # # Check the fault message contains the libvirt error
self.assertIn('Exceeded maximum number of retries', # self.assertIn('Exceeded maximum number of retries',
server['fault']['message']) # server['fault']['message'])
# Check the logs for the exception # # Check the logs for the exception
self.assertIn("Missing required attribute 'iothread'", # self.assertIn("Missing required attribute 'iothread'",
self.stdlog.logger.output) # self.stdlog.logger.output)
self.assertIn("element 'iothreadpin'", self.stdlog.logger.output) # self.assertIn("element 'iothreadpin'", self.stdlog.logger.output)
def test_iothread_pinning_shared_emulator(self): def test_iothread_pinning_shared_emulator(self):
"""Test iothread pinning with shared emulator threads policy.""" """Test iothread pinning with shared emulator threads policy."""
@@ -234,48 +238,48 @@ class TestIOThreadPinningPinnedCPU(
} }
flavor = self._create_flavor(vcpu=2, extra_spec=extra_spec) flavor = self._create_flavor(vcpu=2, extra_spec=extra_spec)
server = self._create_server(
flavor_id=flavor, host='host1', networks='none',
expected_state='ERROR')
# Server should go ACTIVE
# server = self._create_server( # server = self._create_server(
# flavor_id=flavor, host='host1', networks='none', # flavor_id=flavor, host='host1', networks='none',
# expected_state='ACTIVE') # expected_state='ERROR')
# Server should go ACTIVE
server = self._create_server(
flavor_id=flavor, host='host1', networks='none',
expected_state='ACTIVE')
# conn = self.host.driver._host.get_connection() conn = self.host.driver._host.get_connection()
# dom = conn.lookupByUUIDString(server['id']) dom = conn.lookupByUUIDString(server['id'])
# srv_xml = dom.XMLDesc(0) srv_xml = dom.XMLDesc(0)
# # Should have iothreads element # Should have iothreads element
# srv_iothread = self._get_xml_element(srv_xml, './iothreads') srv_iothread = self._get_xml_element(srv_xml, './iothreads')
# self.assertIsNotNone(srv_iothread) self.assertIsNotNone(srv_iothread)
# self.assertEqual('1', srv_iothread.text) self.assertEqual('1', srv_iothread.text)
# # Should have emulatorpin and iothreadpin # Should have emulatorpin and iothreadpin
# srv_emulatorpin = self._get_xml_element( srv_emulatorpin = self._get_xml_element(
# srv_xml, './cputune/emulatorpin') srv_xml, './cputune/emulatorpin')
# srv_iothreadpin = self._get_xml_element( srv_iothreadpin = self._get_xml_element(
# srv_xml, './cputune/iothreadpin') srv_xml, './cputune/iothreadpin')
# self.assertIsNotNone(srv_emulatorpin) self.assertIsNotNone(srv_emulatorpin)
# self.assertIsNotNone(srv_iothreadpin) self.assertIsNotNone(srv_iothreadpin)
# # iothreadpin should have iothread attribute set to 1 # iothreadpin should have iothread attribute set to 1
# self.assertEqual('1', srv_iothreadpin.get('iothread')) self.assertEqual('1', srv_iothreadpin.get('iothread'))
# # Both should be pinned to cpu_shared_set (0-1) # Both should be pinned to cpu_shared_set (0-1)
# self.assertEqual(srv_emulatorpin.get('cpuset'), self.assertEqual(srv_emulatorpin.get('cpuset'),
# srv_iothreadpin.get('cpuset')) srv_iothreadpin.get('cpuset'))
# self.assertEqual('0-1', srv_iothreadpin.get('cpuset')) self.assertEqual('0-1', srv_iothreadpin.get('cpuset'))
self.assertEqual('ERROR', server['status']) # self.assertEqual('ERROR', server['status'])
# Check the fault message contains the libvirt error # # Check the fault message contains the libvirt error
self.assertIn('Exceeded maximum number of retries', # self.assertIn('Exceeded maximum number of retries',
server['fault']['message']) # server['fault']['message'])
# Check the logs for the exception # # Check the logs for the exception
self.assertIn("Missing required attribute 'iothread'", # self.assertIn("Missing required attribute 'iothread'",
self.stdlog.logger.output) # self.stdlog.logger.output)
self.assertIn("element 'iothreadpin'", self.stdlog.logger.output) # self.assertIn("element 'iothreadpin'", self.stdlog.logger.output)
def test_iothread_no_pinning(self): def test_iothread_no_pinning(self):
# No CPU pinning (shared CPUs only) # No CPU pinning (shared CPUs only)
+3 -2
View File
@@ -4054,8 +4054,9 @@ class LibvirtConfigGuestCPUTuneTest(LibvirtConfigBaseTest):
cputune.emulatorpin = emu cputune.emulatorpin = emu
iot = config.LibvirtConfigGuestCPUTuneIOThreadPin() iot = config.LibvirtConfigGuestCPUTuneIOThreadPin()
iot.iothread = 1
iot.cpuset = set([0, 1, 2, 3, 4, 5, 6, 7]) iot.cpuset = set([0, 1, 2, 3, 4, 5, 6, 7])
cputune.iothreadpin = iot cputune.iothreadpin.append(iot)
sch0 = config.LibvirtConfigGuestCPUTuneVCPUSched() sch0 = config.LibvirtConfigGuestCPUTuneVCPUSched()
sch0.vcpus = set([0, 1, 2, 3]) sch0.vcpus = set([0, 1, 2, 3])
@@ -4071,7 +4072,7 @@ class LibvirtConfigGuestCPUTuneTest(LibvirtConfigBaseTest):
self.assertXmlEqual(""" self.assertXmlEqual("""
<cputune> <cputune>
<emulatorpin cpuset="0-7"/> <emulatorpin cpuset="0-7"/>
<iothreadpin cpuset="0-7"/> <iothreadpin iothread="1" cpuset="0-7"/>
<vcpupin vcpu="0" cpuset="0-1"/> <vcpupin vcpu="0" cpuset="0-1"/>
<vcpupin vcpu="1" cpuset="2-3"/> <vcpupin vcpu="1" cpuset="2-3"/>
<vcpupin vcpu="2" cpuset="4-5"/> <vcpupin vcpu="2" cpuset="4-5"/>
+19 -9
View File
@@ -5301,11 +5301,14 @@ class LibvirtConnTestCase(test.NoDBTestCase,
cfg = drvr._get_guest_config(instance_ref, [], cfg = drvr._get_guest_config(instance_ref, [],
image_meta, disk_info) image_meta, disk_info)
self.assertIsInstance(cfg.cputune.iothreadpin, self.assertIsInstance(cfg.cputune.iothreadpin, list)
self.assertEqual(1, len(cfg.cputune.iothreadpin))
self.assertIsInstance(cfg.cputune.iothreadpin[0],
vconfig.LibvirtConfigGuestCPUTuneIOThreadPin) vconfig.LibvirtConfigGuestCPUTuneIOThreadPin)
self.assertEqual(1, cfg.cputune.iothreadpin[0].iothread)
self.assertEqual(cfg.cputune.emulatorpin.cpuset, self.assertEqual(cfg.cputune.emulatorpin.cpuset,
cfg.cputune.iothreadpin.cpuset) cfg.cputune.iothreadpin[0].cpuset)
self.assertEqual(set([6]), cfg.cputune.iothreadpin.cpuset) self.assertEqual(set([6]), cfg.cputune.iothreadpin[0].cpuset)
@mock.patch.object(host.Host, "_check_machine_type", new=mock.Mock()) @mock.patch.object(host.Host, "_check_machine_type", new=mock.Mock())
def test_get_guest_config_iothreadpin_shared_emulator_threads(self): def test_get_guest_config_iothreadpin_shared_emulator_threads(self):
@@ -5356,11 +5359,14 @@ class LibvirtConnTestCase(test.NoDBTestCase,
cfg = drvr._get_guest_config(instance_ref, [], cfg = drvr._get_guest_config(instance_ref, [],
image_meta, disk_info) image_meta, disk_info)
self.assertIsInstance(cfg.cputune.iothreadpin, self.assertIsInstance(cfg.cputune.iothreadpin, list)
self.assertEqual(1, len(cfg.cputune.iothreadpin))
self.assertIsInstance(cfg.cputune.iothreadpin[0],
vconfig.LibvirtConfigGuestCPUTuneIOThreadPin) vconfig.LibvirtConfigGuestCPUTuneIOThreadPin)
self.assertEqual(1, cfg.cputune.iothreadpin[0].iothread)
self.assertEqual(cfg.cputune.emulatorpin.cpuset, self.assertEqual(cfg.cputune.emulatorpin.cpuset,
cfg.cputune.iothreadpin.cpuset) cfg.cputune.iothreadpin[0].cpuset)
self.assertEqual(set([1, 2]), cfg.cputune.iothreadpin.cpuset) self.assertEqual(set([1, 2]), cfg.cputune.iothreadpin[0].cpuset)
@mock.patch.object(host.Host, "_check_machine_type", new=mock.Mock()) @mock.patch.object(host.Host, "_check_machine_type", new=mock.Mock())
def test_get_guest_config_iothreadpin_numa_topology(self): def test_get_guest_config_iothreadpin_numa_topology(self):
@@ -5407,11 +5413,15 @@ class LibvirtConnTestCase(test.NoDBTestCase,
cfg = drvr._get_guest_config(instance_ref, [], cfg = drvr._get_guest_config(instance_ref, [],
image_meta, disk_info) image_meta, disk_info)
self.assertIsInstance(cfg.cputune.iothreadpin, self.assertIsInstance(cfg.cputune.iothreadpin, list)
self.assertEqual(1, len(cfg.cputune.iothreadpin))
self.assertIsInstance(cfg.cputune.iothreadpin[0],
vconfig.LibvirtConfigGuestCPUTuneIOThreadPin) vconfig.LibvirtConfigGuestCPUTuneIOThreadPin)
self.assertEqual(1, cfg.cputune.iothreadpin[0].iothread)
self.assertEqual(cfg.cputune.emulatorpin.cpuset, self.assertEqual(cfg.cputune.emulatorpin.cpuset,
cfg.cputune.iothreadpin.cpuset) cfg.cputune.iothreadpin[0].cpuset)
self.assertEqual(set([0, 1, 2, 3]), cfg.cputune.iothreadpin.cpuset) self.assertEqual(set([0, 1, 2, 3]),
cfg.cputune.iothreadpin[0].cpuset)
def test_get_guest_config_numa_host_instance_shared_emulthreads_err( def test_get_guest_config_numa_host_instance_shared_emulthreads_err(
self): self):
+9 -3
View File
@@ -2675,6 +2675,7 @@ class LibvirtConfigGuestCPUTuneIOThreadPin(LibvirtConfigObject):
**kwargs) **kwargs)
self.cpuset = None self.cpuset = None
self.iothread = None
def format_dom(self): def format_dom(self):
root = super(LibvirtConfigGuestCPUTuneIOThreadPin, self).format_dom() root = super(LibvirtConfigGuestCPUTuneIOThreadPin, self).format_dom()
@@ -2682,6 +2683,8 @@ class LibvirtConfigGuestCPUTuneIOThreadPin(LibvirtConfigObject):
if self.cpuset is not None: if self.cpuset is not None:
root.set("cpuset", root.set("cpuset",
hardware.format_cpu_spec(self.cpuset)) hardware.format_cpu_spec(self.cpuset))
if self.iothread is not None:
root.set("iothread", str(self.iothread))
return root return root
@@ -2721,7 +2724,7 @@ class LibvirtConfigGuestCPUTune(LibvirtConfigObject):
self.period = None self.period = None
self.vcpupin = [] self.vcpupin = []
self.emulatorpin = None self.emulatorpin = None
self.iothreadpin = None self.iothreadpin = []
self.vcpusched = [] self.vcpusched = []
def format_dom(self): def format_dom(self):
@@ -2736,8 +2739,11 @@ class LibvirtConfigGuestCPUTune(LibvirtConfigObject):
if self.emulatorpin is not None: if self.emulatorpin is not None:
root.append(self.emulatorpin.format_dom()) root.append(self.emulatorpin.format_dom())
if self.iothreadpin is not None: # Only render <iothreadpin> if fully configured to avoid bug #2140537:
root.append(self.iothreadpin.format_dom()) # libvirt requires 'iothread' attribute and non-empty 'cpuset'
for pin in self.iothreadpin:
if pin.iothread is not None and pin.cpuset:
root.append(pin.format_dom())
for vcpu in self.vcpupin: for vcpu in self.vcpupin:
root.append(vcpu.format_dom()) root.append(vcpu.format_dom())
for sched in self.vcpusched: for sched in self.vcpusched:
+14 -6
View File
@@ -6462,6 +6462,9 @@ class LibvirtDriver(driver.ComputeDriver):
return emulatorpin_cpuset return emulatorpin_cpuset
def _get_guest_iothread(self):
return 1
def _get_guest_numa_config(self, instance_numa_topology, flavor, def _get_guest_numa_config(self, instance_numa_topology, flavor,
image_meta): image_meta):
"""Returns the config objects for the guest NUMA specs. """Returns the config objects for the guest NUMA specs.
@@ -6532,9 +6535,6 @@ class LibvirtDriver(driver.ComputeDriver):
guest_cpu_tune.emulatorpin = ( guest_cpu_tune.emulatorpin = (
vconfig.LibvirtConfigGuestCPUTuneEmulatorPin()) vconfig.LibvirtConfigGuestCPUTuneEmulatorPin())
guest_cpu_tune.emulatorpin.cpuset = set([]) guest_cpu_tune.emulatorpin.cpuset = set([])
guest_cpu_tune.iothreadpin = (
vconfig.LibvirtConfigGuestCPUTuneIOThreadPin())
guest_cpu_tune.iothreadpin.cpuset = set([])
# Init NUMATune configuration # Init NUMATune configuration
guest_numa_tune = vconfig.LibvirtConfigGuestNUMATune() guest_numa_tune = vconfig.LibvirtConfigGuestNUMATune()
@@ -6578,7 +6578,14 @@ class LibvirtDriver(driver.ComputeDriver):
# both emulator and iothreads are pinned to cores other # both emulator and iothreads are pinned to cores other
# than the instance's cores to support realtime cpus. # than the instance's cores to support realtime cpus.
guest_cpu_tune.emulatorpin.cpuset.update(emu_pin_cpuset) guest_cpu_tune.emulatorpin.cpuset.update(emu_pin_cpuset)
guest_cpu_tune.iothreadpin.cpuset.update(emu_pin_cpuset)
# Create iothreadpin entries after processing all cells
# Use the same cpuset as emulatorpin
for iothread_id in range(self._get_guest_iothread()):
iothread_pin = vconfig.LibvirtConfigGuestCPUTuneIOThreadPin()
iothread_pin.iothread = iothread_id + 1
iothread_pin.cpuset = guest_cpu_tune.emulatorpin.cpuset
guest_cpu_tune.iothreadpin.append(iothread_pin)
# TODO(berrange) When the guest has >1 NUMA node, it will # TODO(berrange) When the guest has >1 NUMA node, it will
# span multiple host NUMA nodes. By pinning emulator threads # span multiple host NUMA nodes. By pinning emulator threads
@@ -7609,8 +7616,9 @@ class LibvirtDriver(driver.ComputeDriver):
self._set_features(guest, instance.os_type, image_meta, flavor) self._set_features(guest, instance.os_type, image_meta, flavor)
self._set_clock(guest, instance.os_type, image_meta) self._set_clock(guest, instance.os_type, image_meta)
# Set IOThreads to 1 for everybody # Set IOThreads to the same value for everybody,
guest.iothreads = 1 # returned by _get_guest_iothread
guest.iothreads = self._get_guest_iothread()
storage_configs = self._get_guest_storage_config(context, storage_configs = self._get_guest_storage_config(context,
instance, image_meta, disk_info, rescue, block_device_info, instance, image_meta, disk_info, rescue, block_device_info,