numa: add numa constraints for emulator threads policy
This commit updates the part responsible to build the NUMA topology constraints to take into account the emulator threads request. implements blueprint libvirt-emulator-threads-policy Change-Id: If2141e9c0b3558bec9122d8f273a7f39b6ded179
This commit is contained in:
@@ -2123,3 +2123,13 @@ class PlacementNotConfigured(NovaException):
|
|||||||
msg_fmt = _("This compute is not configured to talk to the placement "
|
msg_fmt = _("This compute is not configured to talk to the placement "
|
||||||
"service. Configure the [placement] section of nova.conf "
|
"service. Configure the [placement] section of nova.conf "
|
||||||
"and restart the service.")
|
"and restart the service.")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidEmulatorThreadsPolicy(Invalid):
|
||||||
|
msg_fmt = _("CPU emulator threads option requested is invalid, "
|
||||||
|
"given: '%(requested)s', available: '%(available)s'.")
|
||||||
|
|
||||||
|
|
||||||
|
class BadRequirementEmulatorThreadsPolicy(Invalid):
|
||||||
|
msg_fmt = _("An isolated CPU emulator threads option requires a dedicated "
|
||||||
|
"CPU policy option.")
|
||||||
|
|||||||
@@ -1197,6 +1197,66 @@ class NUMATopologyTest(test.NoDBTestCase):
|
|||||||
},
|
},
|
||||||
"expect": exception.RealtimeMaskNotFoundOrInvalid,
|
"expect": exception.RealtimeMaskNotFoundOrInvalid,
|
||||||
},
|
},
|
||||||
|
{ # We pass an invalid option
|
||||||
|
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
|
||||||
|
extra_specs={
|
||||||
|
"hw:emulator_threads_policy": "foo",
|
||||||
|
}),
|
||||||
|
"image": {
|
||||||
|
"properties": {}
|
||||||
|
},
|
||||||
|
"expect": exception.InvalidEmulatorThreadsPolicy,
|
||||||
|
},
|
||||||
|
{ # We request emulator threads option without numa topology
|
||||||
|
"flavor": objects.Flavor(vcpus=16, memory_mb=2048,
|
||||||
|
extra_specs={
|
||||||
|
"hw:emulator_threads_policy": "isolate",
|
||||||
|
}),
|
||||||
|
"image": {
|
||||||
|
"properties": {}
|
||||||
|
},
|
||||||
|
"expect": exception.BadRequirementEmulatorThreadsPolicy,
|
||||||
|
},
|
||||||
|
{ # We request a valid emulator threads options with
|
||||||
|
# cpu_policy based from flavor
|
||||||
|
"flavor": objects.Flavor(vcpus=4, memory_mb=2048,
|
||||||
|
extra_specs={
|
||||||
|
"hw:emulator_threads_policy": "isolate",
|
||||||
|
"hw:cpu_policy": "dedicated",
|
||||||
|
}),
|
||||||
|
"image": {
|
||||||
|
"properties": {}
|
||||||
|
},
|
||||||
|
"expect": objects.InstanceNUMATopology(
|
||||||
|
emulator_threads_policy=
|
||||||
|
fields.CPUEmulatorThreadsPolicy.ISOLATE,
|
||||||
|
cells=[
|
||||||
|
objects.InstanceNUMACell(
|
||||||
|
id=0, cpuset=set([0, 1, 2, 3]), memory=2048,
|
||||||
|
cpu_policy=fields.CPUAllocationPolicy.DEDICATED,
|
||||||
|
)]),
|
||||||
|
},
|
||||||
|
{ # We request a valid emulator threads options with cpu
|
||||||
|
# policy based from image
|
||||||
|
"flavor": objects.Flavor(vcpus=4, memory_mb=2048,
|
||||||
|
extra_specs={
|
||||||
|
"hw:emulator_threads_policy": "isolate",
|
||||||
|
}),
|
||||||
|
"image": {
|
||||||
|
"properties": {
|
||||||
|
"hw_cpu_policy": "dedicated",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expect": objects.InstanceNUMATopology(
|
||||||
|
emulator_threads_policy=
|
||||||
|
fields.CPUEmulatorThreadsPolicy.ISOLATE,
|
||||||
|
cells=[
|
||||||
|
objects.InstanceNUMACell(
|
||||||
|
id=0, cpuset=set([0, 1, 2, 3]), memory=2048,
|
||||||
|
cpu_policy=fields.CPUAllocationPolicy.DEDICATED,
|
||||||
|
)]),
|
||||||
|
},
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for testitem in testdata:
|
for testitem in testdata:
|
||||||
@@ -1216,6 +1276,10 @@ class NUMATopologyTest(test.NoDBTestCase):
|
|||||||
self.assertIsNotNone(topology)
|
self.assertIsNotNone(topology)
|
||||||
self.assertEqual(len(testitem["expect"].cells),
|
self.assertEqual(len(testitem["expect"].cells),
|
||||||
len(topology.cells))
|
len(topology.cells))
|
||||||
|
self.assertEqual(
|
||||||
|
testitem["expect"].emulator_threads_isolated,
|
||||||
|
topology.emulator_threads_isolated)
|
||||||
|
|
||||||
for i in range(len(topology.cells)):
|
for i in range(len(topology.cells)):
|
||||||
self.assertEqual(testitem["expect"].cells[i].id,
|
self.assertEqual(testitem["expect"].cells[i].id,
|
||||||
topology.cells[i].id)
|
topology.cells[i].id)
|
||||||
|
|||||||
@@ -1198,6 +1198,30 @@ def _numa_get_constraints_auto(nodes, flavor):
|
|||||||
return objects.InstanceNUMATopology(cells=cells)
|
return objects.InstanceNUMATopology(cells=cells)
|
||||||
|
|
||||||
|
|
||||||
|
def get_emulator_threads_constraint(flavor, image_meta):
|
||||||
|
"""Determines the emulator threads policy"""
|
||||||
|
emu_threads_policy = flavor.get('extra_specs', {}).get(
|
||||||
|
'hw:emulator_threads_policy')
|
||||||
|
LOG.debug("emulator threads policy constraint: %s", emu_threads_policy)
|
||||||
|
|
||||||
|
if not emu_threads_policy:
|
||||||
|
return
|
||||||
|
|
||||||
|
if emu_threads_policy not in fields.CPUEmulatorThreadsPolicy.ALL:
|
||||||
|
raise exception.InvalidEmulatorThreadsPolicy(
|
||||||
|
requested=emu_threads_policy,
|
||||||
|
available=str(fields.CPUEmulatorThreadsPolicy.ALL))
|
||||||
|
|
||||||
|
if emu_threads_policy == fields.CPUEmulatorThreadsPolicy.ISOLATE:
|
||||||
|
# In order to make available emulator threads policy, a dedicated
|
||||||
|
# CPU policy is necessary.
|
||||||
|
cpu_policy = _get_cpu_policy_constraints(flavor, image_meta)
|
||||||
|
if cpu_policy != fields.CPUAllocationPolicy.DEDICATED:
|
||||||
|
raise exception.BadRequirementEmulatorThreadsPolicy()
|
||||||
|
|
||||||
|
return emu_threads_policy
|
||||||
|
|
||||||
|
|
||||||
def _validate_numa_nodes(nodes):
|
def _validate_numa_nodes(nodes):
|
||||||
"""Validate NUMA nodes number
|
"""Validate NUMA nodes number
|
||||||
|
|
||||||
@@ -1300,6 +1324,7 @@ def numa_get_constraints(flavor, image_meta):
|
|||||||
cpu_policy = _get_cpu_policy_constraints(flavor, image_meta)
|
cpu_policy = _get_cpu_policy_constraints(flavor, image_meta)
|
||||||
cpu_thread_policy = _get_cpu_thread_policy_constraints(flavor, image_meta)
|
cpu_thread_policy = _get_cpu_thread_policy_constraints(flavor, image_meta)
|
||||||
rt_mask = _get_realtime_mask(flavor, image_meta)
|
rt_mask = _get_realtime_mask(flavor, image_meta)
|
||||||
|
emu_thread_policy = get_emulator_threads_constraint(flavor, image_meta)
|
||||||
|
|
||||||
# sanity checks
|
# sanity checks
|
||||||
|
|
||||||
@@ -1329,6 +1354,9 @@ def numa_get_constraints(flavor, image_meta):
|
|||||||
cpu_thread_policy=cpu_thread_policy)
|
cpu_thread_policy=cpu_thread_policy)
|
||||||
numa_topology = objects.InstanceNUMATopology(cells=[single_cell])
|
numa_topology = objects.InstanceNUMATopology(cells=[single_cell])
|
||||||
|
|
||||||
|
if emu_thread_policy:
|
||||||
|
numa_topology.emulator_threads_policy = emu_thread_policy
|
||||||
|
|
||||||
return numa_topology
|
return numa_topology
|
||||||
|
|
||||||
|
|
||||||
@@ -1365,6 +1393,10 @@ def numa_fit_instance_to_host(
|
|||||||
'actual': len(host_topology)})
|
'actual': len(host_topology)})
|
||||||
return
|
return
|
||||||
|
|
||||||
|
emulator_threads_policy = None
|
||||||
|
if 'emulator_threads_policy' in instance_topology:
|
||||||
|
emulator_threads_policy = instance_topology.emulator_threads_policy
|
||||||
|
|
||||||
# TODO(ndipanov): We may want to sort permutations differently
|
# TODO(ndipanov): We may want to sort permutations differently
|
||||||
# depending on whether we want packing/spreading over NUMA nodes
|
# depending on whether we want packing/spreading over NUMA nodes
|
||||||
for host_cell_perm in itertools.permutations(
|
for host_cell_perm in itertools.permutations(
|
||||||
@@ -1389,7 +1421,9 @@ def numa_fit_instance_to_host(
|
|||||||
|
|
||||||
if not pci_requests or ((pci_stats is not None) and
|
if not pci_requests or ((pci_stats is not None) and
|
||||||
pci_stats.support_requests(pci_requests, cells)):
|
pci_stats.support_requests(pci_requests, cells)):
|
||||||
return objects.InstanceNUMATopology(cells=cells)
|
return objects.InstanceNUMATopology(
|
||||||
|
cells=cells,
|
||||||
|
emulator_threads_policy=emulator_threads_policy)
|
||||||
|
|
||||||
|
|
||||||
def numa_get_reserved_huge_pages():
|
def numa_get_reserved_huge_pages():
|
||||||
@@ -1563,8 +1597,11 @@ def instance_topology_from_instance(instance):
|
|||||||
cpu_thread_policy=cell.get('cpu_thread_policy'),
|
cpu_thread_policy=cell.get('cpu_thread_policy'),
|
||||||
cpuset_reserved=cell.get('cpuset_reserved'))
|
cpuset_reserved=cell.get('cpuset_reserved'))
|
||||||
for cell in dict_cells]
|
for cell in dict_cells]
|
||||||
|
emulator_threads_policy = instance_numa_topology.get(
|
||||||
|
'emulator_threads_policy')
|
||||||
instance_numa_topology = objects.InstanceNUMATopology(
|
instance_numa_topology = objects.InstanceNUMATopology(
|
||||||
cells=cells)
|
cells=cells,
|
||||||
|
emulator_threads_policy=emulator_threads_policy)
|
||||||
|
|
||||||
return instance_numa_topology
|
return instance_numa_topology
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user