@@ -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