[PATCH] Multiple changes in guest maxMemory management

This patch includes 3 changes: - Set max_memory limit to 1TB - Set max_memory to 4*Memory - Set memory "hard_limit" tag to (maxMemory + 1Gib) All changes are related to issues found with memory in PCI passthrough or during Live Migration tests. Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> --- model/vms.py | 98 ++++++++++++++++++++++++++++++++++------------------------- osinfo.py | 11 ++++++- vmtemplate.py | 13 ++++++++ 3 files changed, 79 insertions(+), 43 deletions(-) diff --git a/model/vms.py b/model/vms.py index 4835adb..47dd0ce 100644 --- a/model/vms.py +++ b/model/vms.py @@ -57,6 +57,7 @@ from wok.plugins.kimchi.model.utils import get_ascii_nonascii_name, get_vm_name from wok.plugins.kimchi.model.utils import get_metadata_node from wok.plugins.kimchi.model.utils import remove_metadata_node from wok.plugins.kimchi.model.utils import set_metadata_node +from wok.plugins.kimchi.osinfo import MAX_MEM_LIM from wok.plugins.kimchi.screenshot import VMScreenshot from wok.plugins.kimchi.utils import template_name_from_uri from wok.plugins.kimchi.xmlutils.cpu import get_cpu_xml, get_numa_xml @@ -881,49 +882,62 @@ class VMModel(object): else: if memory is not None: root.remove(memory) + + def _get_slots(maxMem): + slots = (maxMem - params['memory']) >> 10 + # Libvirt does not accepts slots <= 1 + if slots < 0: + raise OperationFailed("KCHVM0041E") + elif slots == 0: + slots = 1 + + distro, _, _ = platform.linux_distribution() + if distro == "IBM_PowerKVM": + # max 32 slots on Power + if slots > 32: + slots = 32 + return slots + # End of _get_slots + + def _get_newMaxMem(): + # Setting max memory to 4x memory requested, host total memory, + # or 1 TB. This should avoid problems with live migration + newMaxMem = MAX_MEM_LIM + hostMem = self.conn.get().getInfo()[1] << 10 + if hostMem < newMaxMem: + newMaxMem = hostMem + mem = params.get('memory', 0) + if (mem != 0) and (((mem * 4) << 10) < newMaxMem): + newMaxMem = (mem * 4) << 10 + + distro, _, _ = platform.linux_distribution() + if distro == "IBM_PowerKVM": + # max memory 256MiB alignment + newMaxMem -= (newMaxMem % 256) + return newMaxMem + maxMem = root.find('.maxMemory') - host_mem = self.conn.get().getInfo()[1] - slots = (host_mem - params['memory']) >> 10 - # Libvirt does not accepts slots <= 1 - if slots < 0: - raise OperationFailed("KCHVM0041E") - elif slots == 0: - slots = 1 - - force_max_mem_update = False - distro, _, _ = platform.linux_distribution() - if distro == "IBM_PowerKVM": - # max memory 256MiB alignment - host_mem -= (host_mem % PPC_MEM_ALIGN) - # force max memory update if it exists but it's wrong. - if maxMem is not None and\ - int(maxMem.text) != (host_mem << 10): - force_max_mem_update = True - - # max 32 slots on Power - if slots > 32: - slots = 32 - - if maxMem is None: - max_mem_xml = E.maxMemory( - str(host_mem << 10), - unit='Kib', - slots=str(slots)) - root.insert(0, max_mem_xml) - new_xml = ET.tostring(root, encoding="utf-8") - else: - # Update slots only - new_xml = xml_item_update(ET.tostring(root, encoding="utf-8"), - './maxMemory', - str(slots), - attr='slots') - - if force_max_mem_update: - new_xml = xml_item_update(new_xml, - './maxMemory', - str(host_mem << 10)) - - return new_xml + if maxMem is not None: + root.remove(maxMem) + + # Setting maxMemory + newMaxMem = _get_newMaxMem() + slots = _get_slots(newMaxMem >> 10) + max_mem_xml = E.maxMemory( + str(newMaxMem), + unit='Kib', + slots=str(slots)) + root.insert(0, max_mem_xml) + + # Setting memory hard limit to max_memory + 1GiB + memtune = root.find('memtune') + if memtune is not None: + hl = memtune.find('hard_limit') + if hl is not None: + memtune.remove(hl) + memtune.insert(0, E.hard_limit(str(newMaxMem + 1048576), + unit='Kib')) + return ET.tostring(root, encoding="utf-8") def _get_host_maxcpu(self): diff --git a/osinfo.py b/osinfo.py index 1891398..33757a3 100644 --- a/osinfo.py +++ b/osinfo.py @@ -88,6 +88,9 @@ modern_version_bases = {'x86': {'debian': '6.0', 'ubuntu': '7.10', icon_available_distros = [icon[5:-4] for icon in glob.glob1('%s/images/' % PluginPaths('kimchi').ui_dir, 'icon-*.png')] +# Max memory 1TB, in KiB +MAX_MEM_LIM = 1073741824 + def _get_arch(): for arch, sub_archs in SUPPORTED_ARCHS.iteritems(): @@ -199,7 +202,9 @@ def lookup(distro, version): params['os_version'] = version arch = _get_arch() - # Setting maxMemory of the VM, which will be equal total Host memory in Kib + # Setting maxMemory of the VM, which will be lesser value between: + # [ 1TB, (Template Memory * 4), Host Physical Memory. + # Here, we return 1TB or aligned Host Physical Memory if hasattr(psutil, 'virtual_memory'): params['max_memory'] = psutil.virtual_memory().total >> 10 else: @@ -212,6 +217,10 @@ def lookup(distro, version): alignment = params['max_memory'] % (PPC_MEM_ALIGN << 10) params['max_memory'] -= alignment + # Setting limit to 1TB + if params['max_memory'] > MAX_MEM_LIM: + params['max_memory'] = MAX_MEM_LIM + if distro in modern_version_bases[arch]: if LooseVersion(version) >= LooseVersion( modern_version_bases[arch][distro]): diff --git a/vmtemplate.py b/vmtemplate.py index b90f221..b6e9431 100644 --- a/vmtemplate.py +++ b/vmtemplate.py @@ -352,6 +352,13 @@ class VMTemplate(object): else: params['cdroms'] = cdrom_xml + # In order to avoid problems with live migration, setting maxMemory of + # the VM, which will be lesser value between: + # [ 1TB, (Template Memory * 4), Host Physical Memory. + tmp_max_mem = (params['memory'] << 10) * 4 + if tmp_max_mem < params['max_memory']: + params['max_memory'] = tmp_max_mem + # Setting maximum number of slots to avoid errors when hotplug memory # Number of slots are the numbers of chunks of 1GB that fit inside # the max_memory of the host minus memory assigned to the VM. It @@ -367,6 +374,9 @@ class VMTemplate(object): if distro == "IBM_PowerKVM": params['slots'] = 32 + # set a hard limit using max_memory + 1GiB + params['hard_limit'] = params['max_memory'] + (1024 << 10) + cpu_topo = self.info.get('cpu_info').get('topology') if (cpu_topo is not None): sockets = int(max_vcpus / (cpu_topo['cores'] * @@ -390,6 +400,9 @@ class VMTemplate(object): %(qemu-stream-cmdline)s <name>%(name)s</name> <uuid>%(uuid)s</uuid> + <memtune> + <hard_limit unit='KiB'>%(hard_limit)s</hard_limit> + </memtune> <maxMemory slots='%(slots)s' unit='KiB'>%(max_memory)s</maxMemory> <memory unit='MiB'>%(memory)s</memory> %(vcpus)s -- 2.1.0

Reviewed-by: Paulo Vital <pvital@linux.vnet.ibm.com> On 12/08/2015 02:15 PM, Rodrigo Trujillo wrote:
This patch includes 3 changes: - Set max_memory limit to 1TB - Set max_memory to 4*Memory - Set memory "hard_limit" tag to (maxMemory + 1Gib)
All changes are related to issues found with memory in PCI passthrough or during Live Migration tests.
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> --- model/vms.py | 98 ++++++++++++++++++++++++++++++++++------------------------- osinfo.py | 11 ++++++- vmtemplate.py | 13 ++++++++ 3 files changed, 79 insertions(+), 43 deletions(-)
diff --git a/model/vms.py b/model/vms.py index 4835adb..47dd0ce 100644 --- a/model/vms.py +++ b/model/vms.py @@ -57,6 +57,7 @@ from wok.plugins.kimchi.model.utils import get_ascii_nonascii_name, get_vm_name from wok.plugins.kimchi.model.utils import get_metadata_node from wok.plugins.kimchi.model.utils import remove_metadata_node from wok.plugins.kimchi.model.utils import set_metadata_node +from wok.plugins.kimchi.osinfo import MAX_MEM_LIM from wok.plugins.kimchi.screenshot import VMScreenshot from wok.plugins.kimchi.utils import template_name_from_uri from wok.plugins.kimchi.xmlutils.cpu import get_cpu_xml, get_numa_xml @@ -881,49 +882,62 @@ class VMModel(object): else: if memory is not None: root.remove(memory) + + def _get_slots(maxMem): + slots = (maxMem - params['memory']) >> 10 + # Libvirt does not accepts slots <= 1 + if slots < 0: + raise OperationFailed("KCHVM0041E") + elif slots == 0: + slots = 1 + + distro, _, _ = platform.linux_distribution() + if distro == "IBM_PowerKVM": + # max 32 slots on Power + if slots > 32: + slots = 32 + return slots + # End of _get_slots + + def _get_newMaxMem(): + # Setting max memory to 4x memory requested, host total memory, + # or 1 TB. This should avoid problems with live migration + newMaxMem = MAX_MEM_LIM + hostMem = self.conn.get().getInfo()[1] << 10 + if hostMem < newMaxMem: + newMaxMem = hostMem + mem = params.get('memory', 0) + if (mem != 0) and (((mem * 4) << 10) < newMaxMem): + newMaxMem = (mem * 4) << 10 + + distro, _, _ = platform.linux_distribution() + if distro == "IBM_PowerKVM": + # max memory 256MiB alignment + newMaxMem -= (newMaxMem % 256) + return newMaxMem + maxMem = root.find('.maxMemory') - host_mem = self.conn.get().getInfo()[1] - slots = (host_mem - params['memory']) >> 10 - # Libvirt does not accepts slots <= 1 - if slots < 0: - raise OperationFailed("KCHVM0041E") - elif slots == 0: - slots = 1 - - force_max_mem_update = False - distro, _, _ = platform.linux_distribution() - if distro == "IBM_PowerKVM": - # max memory 256MiB alignment - host_mem -= (host_mem % PPC_MEM_ALIGN) - # force max memory update if it exists but it's wrong. - if maxMem is not None and\ - int(maxMem.text) != (host_mem << 10): - force_max_mem_update = True - - # max 32 slots on Power - if slots > 32: - slots = 32 - - if maxMem is None: - max_mem_xml = E.maxMemory( - str(host_mem << 10), - unit='Kib', - slots=str(slots)) - root.insert(0, max_mem_xml) - new_xml = ET.tostring(root, encoding="utf-8") - else: - # Update slots only - new_xml = xml_item_update(ET.tostring(root, encoding="utf-8"), - './maxMemory', - str(slots), - attr='slots') - - if force_max_mem_update: - new_xml = xml_item_update(new_xml, - './maxMemory', - str(host_mem << 10)) - - return new_xml + if maxMem is not None: + root.remove(maxMem) + + # Setting maxMemory + newMaxMem = _get_newMaxMem() + slots = _get_slots(newMaxMem >> 10) + max_mem_xml = E.maxMemory( + str(newMaxMem), + unit='Kib', + slots=str(slots)) + root.insert(0, max_mem_xml) + + # Setting memory hard limit to max_memory + 1GiB + memtune = root.find('memtune') + if memtune is not None: + hl = memtune.find('hard_limit') + if hl is not None: + memtune.remove(hl) + memtune.insert(0, E.hard_limit(str(newMaxMem + 1048576), + unit='Kib')) + return ET.tostring(root, encoding="utf-8")
def _get_host_maxcpu(self): diff --git a/osinfo.py b/osinfo.py index 1891398..33757a3 100644 --- a/osinfo.py +++ b/osinfo.py @@ -88,6 +88,9 @@ modern_version_bases = {'x86': {'debian': '6.0', 'ubuntu': '7.10', icon_available_distros = [icon[5:-4] for icon in glob.glob1('%s/images/' % PluginPaths('kimchi').ui_dir, 'icon-*.png')]
+# Max memory 1TB, in KiB +MAX_MEM_LIM = 1073741824 +
def _get_arch(): for arch, sub_archs in SUPPORTED_ARCHS.iteritems(): @@ -199,7 +202,9 @@ def lookup(distro, version): params['os_version'] = version arch = _get_arch()
- # Setting maxMemory of the VM, which will be equal total Host memory in Kib + # Setting maxMemory of the VM, which will be lesser value between: + # [ 1TB, (Template Memory * 4), Host Physical Memory. + # Here, we return 1TB or aligned Host Physical Memory if hasattr(psutil, 'virtual_memory'): params['max_memory'] = psutil.virtual_memory().total >> 10 else: @@ -212,6 +217,10 @@ def lookup(distro, version): alignment = params['max_memory'] % (PPC_MEM_ALIGN << 10) params['max_memory'] -= alignment
+ # Setting limit to 1TB + if params['max_memory'] > MAX_MEM_LIM: + params['max_memory'] = MAX_MEM_LIM + if distro in modern_version_bases[arch]: if LooseVersion(version) >= LooseVersion( modern_version_bases[arch][distro]): diff --git a/vmtemplate.py b/vmtemplate.py index b90f221..b6e9431 100644 --- a/vmtemplate.py +++ b/vmtemplate.py @@ -352,6 +352,13 @@ class VMTemplate(object): else: params['cdroms'] = cdrom_xml
+ # In order to avoid problems with live migration, setting maxMemory of + # the VM, which will be lesser value between: + # [ 1TB, (Template Memory * 4), Host Physical Memory. + tmp_max_mem = (params['memory'] << 10) * 4 + if tmp_max_mem < params['max_memory']: + params['max_memory'] = tmp_max_mem + # Setting maximum number of slots to avoid errors when hotplug memory # Number of slots are the numbers of chunks of 1GB that fit inside # the max_memory of the host minus memory assigned to the VM. It @@ -367,6 +374,9 @@ class VMTemplate(object): if distro == "IBM_PowerKVM": params['slots'] = 32
+ # set a hard limit using max_memory + 1GiB + params['hard_limit'] = params['max_memory'] + (1024 << 10) + cpu_topo = self.info.get('cpu_info').get('topology') if (cpu_topo is not None): sockets = int(max_vcpus / (cpu_topo['cores'] * @@ -390,6 +400,9 @@ class VMTemplate(object): %(qemu-stream-cmdline)s <name>%(name)s</name> <uuid>%(uuid)s</uuid> + <memtune> + <hard_limit unit='KiB'>%(hard_limit)s</hard_limit> + </memtune> <maxMemory slots='%(slots)s' unit='KiB'>%(max_memory)s</maxMemory> <memory unit='MiB'>%(memory)s</memory> %(vcpus)s
participants (3)
-
Aline Manera
-
Paulo Vital
-
Rodrigo Trujillo