hardware: Invert order of NUMA topology generation

Previously, we were generating an instance NUMA topology and then
tacking things like CPU pinning policies onto these topologies. Going
forward, we'll want to use the latter to inform the former. Set that up
now by simply inverting how we do things. There's no end-user impact
other than improved docstrings for some functions.

Part of blueprint use-pcpu-and-vcpu-in-one-instance

Change-Id: I142bf203d5751864afeb3da35efd708f24c08924
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane
2020-03-11 17:33:58 +00:00
parent c88a35e8ea
commit eedac34b21
+73 -48
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