Merge "virt: reserved hugepages on compute host"
This commit is contained in:
@@ -299,6 +299,29 @@ timeout_nbd = cfg.IntOpt(
|
|||||||
help='Amount of time, in seconds, to wait for NBD '
|
help='Amount of time, in seconds, to wait for NBD '
|
||||||
'device start up.')
|
'device start up.')
|
||||||
|
|
||||||
|
reserved_memory_pages = cfg.MultiStrOpt(
|
||||||
|
"reserved_memory_pages",
|
||||||
|
default=[],
|
||||||
|
help="""Reserves amount of huge pages per NUMA host cells
|
||||||
|
|
||||||
|
Possible values:
|
||||||
|
|
||||||
|
* A list of valid strings which reflect NUMA node ID, page size and
|
||||||
|
number of pages reserved separated by punctuation mark
|
||||||
|
colon. Default unit is KiB.
|
||||||
|
|
||||||
|
reserved_memory_pages = ["0:2MB:64", "1:1GB:1"]
|
||||||
|
|
||||||
|
Services which consume this:
|
||||||
|
|
||||||
|
* nova-compute
|
||||||
|
* nova-scheduler
|
||||||
|
|
||||||
|
Related options:
|
||||||
|
|
||||||
|
* None""")
|
||||||
|
|
||||||
|
|
||||||
ALL_OPTS = [vcpu_pin_set,
|
ALL_OPTS = [vcpu_pin_set,
|
||||||
compute_driver,
|
compute_driver,
|
||||||
default_ephemeral_format,
|
default_ephemeral_format,
|
||||||
@@ -312,7 +335,8 @@ ALL_OPTS = [vcpu_pin_set,
|
|||||||
injected_network_template,
|
injected_network_template,
|
||||||
virt_mkfs,
|
virt_mkfs,
|
||||||
resize_fs_using_block_device,
|
resize_fs_using_block_device,
|
||||||
timeout_nbd]
|
timeout_nbd,
|
||||||
|
reserved_memory_pages]
|
||||||
|
|
||||||
|
|
||||||
def register_opts(conf):
|
def register_opts(conf):
|
||||||
|
@@ -2116,3 +2116,9 @@ class OsInfoNotFound(NotFound):
|
|||||||
|
|
||||||
class BuildRequestNotFound(NotFound):
|
class BuildRequestNotFound(NotFound):
|
||||||
msg_fmt = _("BuildRequest not found for instance %(uuid)s")
|
msg_fmt = _("BuildRequest not found for instance %(uuid)s")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidReservedMemoryPagesOption(Invalid):
|
||||||
|
msg_fmt = _("The format of the option 'reserved_memory_pages' is invalid. "
|
||||||
|
"(found '%(conf)s') Please refer to the nova "
|
||||||
|
"config-reference.")
|
||||||
|
@@ -1349,6 +1349,71 @@ class NUMATopologyTest(test.NoDBTestCase):
|
|||||||
self.assertEqual(hostusage.cells[2].cpu_usage, 0)
|
self.assertEqual(hostusage.cells[2].cpu_usage, 0)
|
||||||
self.assertEqual(hostusage.cells[2].memory_usage, 0)
|
self.assertEqual(hostusage.cells[2].memory_usage, 0)
|
||||||
|
|
||||||
|
def test_topo_usage_reserved_page_size(self):
|
||||||
|
hosttopo = objects.NUMATopology(cells=[
|
||||||
|
objects.NUMACell(id=0, cpuset=set([0, 1]), memory=512,
|
||||||
|
cpu_usage=0, memory_usage=0, mempages=[
|
||||||
|
objects.NUMAPagesTopology(
|
||||||
|
size_kb=2048,
|
||||||
|
total=512,
|
||||||
|
used=128)],
|
||||||
|
siblings=[], pinned_cpus=set([])),
|
||||||
|
objects.NUMACell(id=1, cpuset=set([2, 3]), memory=512,
|
||||||
|
cpu_usage=0, memory_usage=0, mempages=[
|
||||||
|
objects.NUMAPagesTopology(
|
||||||
|
size_kb=1048576,
|
||||||
|
total=5,
|
||||||
|
used=2)],
|
||||||
|
siblings=[], pinned_cpus=set([])),
|
||||||
|
])
|
||||||
|
instance1 = objects.InstanceNUMATopology(cells=[
|
||||||
|
objects.InstanceNUMACell(
|
||||||
|
id=0, cpuset=set([0, 1]), memory=256, pagesize=2048),
|
||||||
|
objects.InstanceNUMACell(
|
||||||
|
id=1, cpuset=set([2, 3]), memory=1024, pagesize=1048576),
|
||||||
|
])
|
||||||
|
|
||||||
|
# reserved_page_size is using a global variable so we prefer
|
||||||
|
# to create tests in a same context to avoid errors when
|
||||||
|
# running unit tests in parallel
|
||||||
|
|
||||||
|
def test_success():
|
||||||
|
hw.RESERVED_MEMORY_PAGES = None
|
||||||
|
self.flags(reserved_memory_pages=["0:2048:128",
|
||||||
|
"1:1048576:1"])
|
||||||
|
hostusage = hw.numa_usage_from_instances(
|
||||||
|
hosttopo, [instance1])
|
||||||
|
|
||||||
|
self.assertEqual(hostusage.cells[0].mempages[0].size_kb, 2048)
|
||||||
|
self.assertEqual(hostusage.cells[0].mempages[0].total, 512)
|
||||||
|
# 128 reserved + 128 used by instance
|
||||||
|
self.assertEqual(hostusage.cells[0].mempages[0].used, 256)
|
||||||
|
|
||||||
|
self.assertEqual(hostusage.cells[1].mempages[0].size_kb, 1048576)
|
||||||
|
self.assertEqual(hostusage.cells[1].mempages[0].total, 5)
|
||||||
|
# 1 reserved + 1 used by instance + 2 already used:
|
||||||
|
self.assertEqual(hostusage.cells[1].mempages[0].used, 4)
|
||||||
|
|
||||||
|
def test_invalid_format():
|
||||||
|
hw.RESERVED_MEMORY_PAGES = None
|
||||||
|
self.flags(reserved_memory_pages="1:2:3")
|
||||||
|
self.assertRaises(
|
||||||
|
exception.InvalidReservedMemoryPagesOption,
|
||||||
|
hw.numa_usage_from_instances,
|
||||||
|
hosttopo, [instance1])
|
||||||
|
|
||||||
|
def test_invalid_value():
|
||||||
|
hw.RESERVED_MEMORY_PAGES = None
|
||||||
|
self.flags(reserved_memory_pages=["0:foo:bar"])
|
||||||
|
self.assertRaises(
|
||||||
|
exception.InvalidReservedMemoryPagesOption,
|
||||||
|
hw.numa_usage_from_instances,
|
||||||
|
hosttopo, [instance1])
|
||||||
|
|
||||||
|
test_success()
|
||||||
|
test_invalid_format()
|
||||||
|
test_invalid_value()
|
||||||
|
|
||||||
def test_topo_usage_none(self):
|
def test_topo_usage_none(self):
|
||||||
hosttopo = objects.NUMATopology(cells=[
|
hosttopo = objects.NUMATopology(cells=[
|
||||||
objects.NUMACell(id=0, cpuset=set([0, 1]), memory=512,
|
objects.NUMACell(id=0, cpuset=set([0, 1]), memory=512,
|
||||||
|
@@ -38,6 +38,8 @@ MEMPAGES_SMALL = -1
|
|||||||
MEMPAGES_LARGE = -2
|
MEMPAGES_LARGE = -2
|
||||||
MEMPAGES_ANY = -3
|
MEMPAGES_ANY = -3
|
||||||
|
|
||||||
|
RESERVED_MEMORY_PAGES = None
|
||||||
|
|
||||||
|
|
||||||
def get_vcpu_pin_set():
|
def get_vcpu_pin_set():
|
||||||
"""Parsing vcpu_pin_set config.
|
"""Parsing vcpu_pin_set config.
|
||||||
@@ -1248,6 +1250,36 @@ def numa_fit_instance_to_host(
|
|||||||
return objects.InstanceNUMATopology(cells=cells)
|
return objects.InstanceNUMATopology(cells=cells)
|
||||||
|
|
||||||
|
|
||||||
|
def _numa_reserved_memory_pages_from_cell(cell_id, pagesize):
|
||||||
|
global RESERVED_MEMORY_PAGES
|
||||||
|
try:
|
||||||
|
if CONF.reserved_memory_pages and not RESERVED_MEMORY_PAGES:
|
||||||
|
RESERVED_MEMORY_PAGES = collections.defaultdict(dict)
|
||||||
|
for cfg in CONF.reserved_memory_pages:
|
||||||
|
node, pagesize, reserved = cfg.split(":", 2)
|
||||||
|
node = int(node)
|
||||||
|
try:
|
||||||
|
pagesize = int(pagesize)
|
||||||
|
except ValueError:
|
||||||
|
pagesize = strutils.string_to_bytes(
|
||||||
|
pagesize, return_int=True) / units.Ki
|
||||||
|
RESERVED_MEMORY_PAGES[int(node)][pagesize] = int(reserved)
|
||||||
|
if RESERVED_MEMORY_PAGES:
|
||||||
|
return RESERVED_MEMORY_PAGES.get(cell_id, {}).get(pagesize, 0)
|
||||||
|
except ValueError:
|
||||||
|
raise exception.InvalidReservedMemoryPagesOption(
|
||||||
|
conf=CONF.reserved_memory_pages)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def _numa_reserved_memory_pages(hostcells):
|
||||||
|
for hostcell in hostcells:
|
||||||
|
for pages in hostcell.mempages:
|
||||||
|
pages.used = (
|
||||||
|
pages.used + _numa_reserved_memory_pages_from_cell(
|
||||||
|
hostcell.id, pages.size_kb))
|
||||||
|
|
||||||
|
|
||||||
def _numa_pagesize_usage_from_cell(hostcell, instancecell, sign):
|
def _numa_pagesize_usage_from_cell(hostcell, instancecell, sign):
|
||||||
topo = []
|
topo = []
|
||||||
for pages in hostcell.mempages:
|
for pages in hostcell.mempages:
|
||||||
@@ -1326,6 +1358,9 @@ def numa_usage_from_instances(host, instances, free=False):
|
|||||||
|
|
||||||
cells.append(newcell)
|
cells.append(newcell)
|
||||||
|
|
||||||
|
# Compute reserved memory pages
|
||||||
|
_numa_reserved_memory_pages(cells)
|
||||||
|
|
||||||
return objects.NUMATopology(cells=cells)
|
return objects.NUMATopology(cells=cells)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Adds reserved_memory_pages option to reserve
|
||||||
|
amount of hugepages used by third part components.
|
Reference in New Issue
Block a user