Skip to content

Commit eedac34

Browse files
committed
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 <[email protected]>
1 parent c88a35e commit eedac34

File tree

1 file changed

+74
-49
lines changed

1 file changed

+74
-49
lines changed

nova/virt/hardware.py

Lines changed: 74 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,9 +1575,20 @@ def get_cpu_thread_policy_constraint(
15751575
return policy
15761576

15771577

1578-
def _get_numa_topology_auto(nodes, flavor):
1579-
if ((flavor.vcpus % nodes) > 0 or
1580-
(flavor.memory_mb % nodes) > 0):
1578+
def _get_numa_topology_auto(
1579+
nodes: int,
1580+
flavor: 'objects.Flavor',
1581+
) -> 'objects.InstanceNUMATopology':
1582+
"""Generate a NUMA topology automatically based on CPUs and memory.
1583+
1584+
This is "automatic" because there's no user-provided per-node configuration
1585+
here - it's all auto-generated based on the number of nodes.
1586+
1587+
:param nodes: The number of nodes required in the generated topology.
1588+
:param flavor: The flavor used for the instance, from which to extract the
1589+
CPU and memory count.
1590+
"""
1591+
if (flavor.vcpus % nodes) > 0 or (flavor.memory_mb % nodes) > 0:
15811592
raise exception.ImageNUMATopologyAsymmetric()
15821593

15831594
cells = []
@@ -1593,7 +1604,23 @@ def _get_numa_topology_auto(nodes, flavor):
15931604
return objects.InstanceNUMATopology(cells=cells)
15941605

15951606

1596-
def _get_numa_topology_manual(nodes, flavor, cpu_list, mem_list):
1607+
def _get_numa_topology_manual(
1608+
nodes: int,
1609+
flavor: 'objects.Flavor',
1610+
cpu_list: ty.List[ty.Set[int]],
1611+
mem_list: ty.List[int],
1612+
) -> 'objects.InstanceNUMATopology':
1613+
"""Generate a NUMA topology based on user-provided NUMA topology hints.
1614+
1615+
:param nodes: The number of nodes required in the generated topology.
1616+
:param flavor: The flavor used for the instance, from which to extract the
1617+
CPU and memory count.
1618+
:param cpu_list: A list of sets of ints; each set in the list corresponds
1619+
to the set of guest cores to assign to NUMA node $index.
1620+
:param mem_list: A list of ints; each int corresponds to the amount of
1621+
memory to assign to NUMA node $index.
1622+
:returns: The generated instance NUMA topology.
1623+
"""
15971624
cells = []
15981625
totalmem = 0
15991626

@@ -1810,36 +1837,6 @@ def numa_get_constraints(flavor, image_meta):
18101837
and implicitly requested resources of hyperthreading traits
18111838
:returns: objects.InstanceNUMATopology, or None
18121839
"""
1813-
numa_topology = None
1814-
1815-
nodes = _get_numa_node_count_constraint(flavor, image_meta)
1816-
pagesize = _get_numa_pagesize_constraint(flavor, image_meta)
1817-
vpmems = get_vpmems(flavor)
1818-
1819-
if nodes or pagesize or vpmems:
1820-
nodes = nodes or 1
1821-
1822-
cpu_list = _get_numa_cpu_constraint(flavor, image_meta)
1823-
mem_list = _get_numa_mem_constraint(flavor, image_meta)
1824-
1825-
if cpu_list is None and mem_list is None:
1826-
numa_topology = _get_numa_topology_auto(
1827-
nodes, flavor)
1828-
elif cpu_list is not None and mem_list is not None:
1829-
# If any node has data set, all nodes must have data set
1830-
if len(cpu_list) != nodes or len(mem_list) != nodes:
1831-
raise exception.ImageNUMATopologyIncomplete()
1832-
1833-
numa_topology = _get_numa_topology_manual(
1834-
nodes, flavor, cpu_list, mem_list
1835-
)
1836-
else:
1837-
# If one property list is specified both must be
1838-
raise exception.ImageNUMATopologyIncomplete()
1839-
1840-
# We currently support same pagesize for all cells.
1841-
for c in numa_topology.cells:
1842-
setattr(c, 'pagesize', pagesize)
18431840

18441841
cpu_policy = get_cpu_policy_constraint(flavor, image_meta)
18451842
cpu_thread_policy = get_cpu_thread_policy_constraint(flavor, image_meta)
@@ -1912,23 +1909,51 @@ def numa_get_constraints(flavor, image_meta):
19121909
if rt_mask:
19131910
raise exception.RealtimeConfigurationInvalid()
19141911

1915-
return numa_topology
1912+
nodes = _get_numa_node_count_constraint(flavor, image_meta)
1913+
pagesize = _get_numa_pagesize_constraint(flavor, image_meta)
1914+
vpmems = get_vpmems(flavor)
19161915

1917-
if numa_topology:
1918-
for cell in numa_topology.cells:
1919-
cell.cpu_policy = cpu_policy
1920-
cell.cpu_thread_policy = cpu_thread_policy
1921-
else:
1922-
single_cell = objects.InstanceNUMACell(
1923-
id=0,
1924-
cpuset=set(range(flavor.vcpus)),
1925-
memory=flavor.memory_mb,
1926-
cpu_policy=cpu_policy,
1927-
cpu_thread_policy=cpu_thread_policy)
1928-
numa_topology = objects.InstanceNUMATopology(cells=[single_cell])
1929-
1930-
if emu_threads_policy:
1916+
# NOTE(stephenfin): There are currently four things that will configure a
1917+
# NUMA topology for an instance:
1918+
#
1919+
# - The user explicitly requesting one
1920+
# - The use of CPU pinning
1921+
# - The use of hugepages
1922+
# - The use of vPMEM
1923+
if nodes or pagesize or vpmems or cpu_policy in (
1924+
fields.CPUAllocationPolicy.DEDICATED,
1925+
):
1926+
nodes = nodes or 1
1927+
1928+
cpu_list = _get_numa_cpu_constraint(flavor, image_meta)
1929+
mem_list = _get_numa_mem_constraint(flavor, image_meta)
1930+
1931+
if cpu_list is None and mem_list is None:
1932+
numa_topology = _get_numa_topology_auto(nodes, flavor)
1933+
elif cpu_list is not None and mem_list is not None:
1934+
# If any node has data set, all nodes must have data set
1935+
if len(cpu_list) != nodes or len(mem_list) != nodes:
1936+
raise exception.ImageNUMATopologyIncomplete()
1937+
1938+
numa_topology = _get_numa_topology_manual(
1939+
nodes, flavor, cpu_list, mem_list
1940+
)
1941+
else:
1942+
# If one property list is specified both must be
1943+
raise exception.ImageNUMATopologyIncomplete()
1944+
1945+
# We currently support the same pagesize, CPU policy and CPU thread
1946+
# policy for all cells, but these are still stored on a per-cell
1947+
# basis :(
1948+
for c in numa_topology.cells:
1949+
setattr(c, 'pagesize', pagesize)
1950+
setattr(c, 'cpu_policy', cpu_policy)
1951+
setattr(c, 'cpu_thread_policy', cpu_thread_policy)
1952+
1953+
# ...but emulator threads policy is not \o/
19311954
numa_topology.emulator_threads_policy = emu_threads_policy
1955+
else:
1956+
numa_topology = None
19321957

19331958
return numa_topology
19341959

0 commit comments

Comments
 (0)