Merge "hardware: Invert order of NUMA topology generation"

This commit is contained in:
Zuul 2020-07-09 14:12:30 +00:00 committed by Gerrit Code Review
commit 6d6b8b4522
1 changed files with 73 additions and 48 deletions

View File

@ -1575,9 +1575,20 @@ def get_cpu_thread_policy_constraint(
return policy
def _get_numa_topology_auto(nodes, flavor):
if ((flavor.vcpus % nodes) > 0 or
(flavor.memory_mb % nodes) > 0):
def _get_numa_topology_auto(
nodes: int,
flavor: 'objects.Flavor',
) -> 'objects.InstanceNUMATopology':
"""Generate a NUMA topology automatically based on CPUs and memory.
This is "automatic" because there's no user-provided per-node configuration
here - it's all auto-generated based on the number of nodes.
:param nodes: The number of nodes required in the generated topology.
:param flavor: The flavor used for the instance, from which to extract the
CPU and memory count.
"""
if (flavor.vcpus % nodes) > 0 or (flavor.memory_mb % nodes) > 0:
raise exception.ImageNUMATopologyAsymmetric()
cells = []
@ -1593,7 +1604,23 @@ def _get_numa_topology_auto(nodes, flavor):
return objects.InstanceNUMATopology(cells=cells)
def _get_numa_topology_manual(nodes, flavor, cpu_list, mem_list):
def _get_numa_topology_manual(
nodes: int,
flavor: 'objects.Flavor',
cpu_list: ty.List[ty.Set[int]],
mem_list: ty.List[int],
) -> 'objects.InstanceNUMATopology':
"""Generate a NUMA topology based on user-provided NUMA topology hints.
:param nodes: The number of nodes required in the generated topology.
:param flavor: The flavor used for the instance, from which to extract the
CPU and memory count.
:param cpu_list: A list of sets of ints; each set in the list corresponds
to the set of guest cores to assign to NUMA node $index.
:param mem_list: A list of ints; each int corresponds to the amount of
memory to assign to NUMA node $index.
:returns: The generated instance NUMA topology.
"""
cells = []
totalmem = 0
@ -1810,36 +1837,6 @@ def numa_get_constraints(flavor, image_meta):
and implicitly requested resources of hyperthreading traits
:returns: objects.InstanceNUMATopology, or None
"""
numa_topology = None
nodes = _get_numa_node_count_constraint(flavor, image_meta)
pagesize = _get_numa_pagesize_constraint(flavor, image_meta)
vpmems = get_vpmems(flavor)
if nodes or pagesize or vpmems:
nodes = nodes or 1
cpu_list = _get_numa_cpu_constraint(flavor, image_meta)
mem_list = _get_numa_mem_constraint(flavor, image_meta)
if cpu_list is None and mem_list is None:
numa_topology = _get_numa_topology_auto(
nodes, flavor)
elif cpu_list is not None and mem_list is not None:
# If any node has data set, all nodes must have data set
if len(cpu_list) != nodes or len(mem_list) != nodes:
raise exception.ImageNUMATopologyIncomplete()
numa_topology = _get_numa_topology_manual(
nodes, flavor, cpu_list, mem_list
)
else:
# If one property list is specified both must be
raise exception.ImageNUMATopologyIncomplete()
# We currently support same pagesize for all cells.
for c in numa_topology.cells:
setattr(c, 'pagesize', pagesize)
cpu_policy = get_cpu_policy_constraint(flavor, image_meta)
cpu_thread_policy = get_cpu_thread_policy_constraint(flavor, image_meta)
@ -1912,23 +1909,51 @@ def numa_get_constraints(flavor, image_meta):
if rt_mask:
raise exception.RealtimeConfigurationInvalid()
return numa_topology
nodes = _get_numa_node_count_constraint(flavor, image_meta)
pagesize = _get_numa_pagesize_constraint(flavor, image_meta)
vpmems = get_vpmems(flavor)
if numa_topology:
for cell in numa_topology.cells:
cell.cpu_policy = cpu_policy
cell.cpu_thread_policy = cpu_thread_policy
else:
single_cell = objects.InstanceNUMACell(
id=0,
cpuset=set(range(flavor.vcpus)),
memory=flavor.memory_mb,
cpu_policy=cpu_policy,
cpu_thread_policy=cpu_thread_policy)
numa_topology = objects.InstanceNUMATopology(cells=[single_cell])
# NOTE(stephenfin): There are currently four things that will configure a
# NUMA topology for an instance:
#
# - The user explicitly requesting one
# - The use of CPU pinning
# - The use of hugepages
# - The use of vPMEM
if nodes or pagesize or vpmems or cpu_policy in (
fields.CPUAllocationPolicy.DEDICATED,
):
nodes = nodes or 1
if emu_threads_policy:
cpu_list = _get_numa_cpu_constraint(flavor, image_meta)
mem_list = _get_numa_mem_constraint(flavor, image_meta)
if cpu_list is None and mem_list is None:
numa_topology = _get_numa_topology_auto(nodes, flavor)
elif cpu_list is not None and mem_list is not None:
# If any node has data set, all nodes must have data set
if len(cpu_list) != nodes or len(mem_list) != nodes:
raise exception.ImageNUMATopologyIncomplete()
numa_topology = _get_numa_topology_manual(
nodes, flavor, cpu_list, mem_list
)
else:
# If one property list is specified both must be
raise exception.ImageNUMATopologyIncomplete()
# We currently support the same pagesize, CPU policy and CPU thread
# policy for all cells, but these are still stored on a per-cell
# basis :(
for c in numa_topology.cells:
setattr(c, 'pagesize', pagesize)
setattr(c, 'cpu_policy', cpu_policy)
setattr(c, 'cpu_thread_policy', cpu_thread_policy)
# ...but emulator threads policy is not \o/
numa_topology.emulator_threads_policy = emu_threads_policy
else:
numa_topology = None
return numa_topology