FYI, test applied: http://autotest.kernel.org/changeset/4264 On Mon, Feb 22, 2010 at 5:29 PM, Lucas Meneghel Rodrigues <lmr@xxxxxxxxxx> wrote: > This is an implementation of KSM testing. The basic idea behind the > test is to start guests, copy the script allocator.py to them. Once > executed, the process accepts input commands on its main loop. > > The script will allow to fill up memory pages of the guests, according > to patterns, this way it's possible to have a large portion of guests > memory exactly the same, so KSM can do memory merge. > > Then we can easily split memory by filling memory of some guests with > other values, and verify how KSM behaves according to each operation. > > Test : > a] serial > 1) initialize, merge all mem to single page > 2) separate first guset mem > 3) separate rest of guest up to fill all mem > 4) kill all guests except for the last > 5) check if mem of last guest is ok > 6) kill guest > > b] parallel > 1) initialize, merge all mem to single page > 2) separate mem of guest > 3) verification of guest mem > 4) merge mem to one block > 5) verification of guests mem > 6) separate mem of guests by 96B > 7) check if mem is all right > 8) kill guest > > allocator.py (client side script) > After start they wait for command witch they make in client side. > mem_fill class implement commands to fill, check mem and return > error to host. > > Notes: > Jiri and Lukáš, please verify this last version, we are close > to commiting this test, at last. > > Changelog: > > v4 - Cleanup and bugfixing for the test, after a good round of > testing: > * Moved the host_reserve_memory and guest_reserve_memory to > the config file, in order to have more flexibility. The 256MB > default memory reserve for guests was making guests to run out > of memory and trigger the kernel OOM killer, frequently killing > the ssh daemon and ruining the test, on a bare bones Fedora 12 > guest. > * Fixed up debug and info messages in general to be more clear > and consistent > * The parallel test had 2 repeated operations, mem_fill(value) > and another mem_fill(value). From the logging messages, it > became clear the author meant a mem_fill(value) followed by a > mem_check(value) > * Made MemFill.check_value() to accept a value, suitable for > testing memory contents that were not filled by the default > static value > * Factored allocator.py operations to a function, saved up a > good deal of code. > * General code cleanup and coding style > > Signed-off-by: Jiri Zupka <jzupka@xxxxxxxxxx> > Signed-off-by: Lukáš Doktor<ldoktor@xxxxxxxxxx> > Signed-off-by: Lucas Meneghel Rodrigues <lmr@xxxxxxxxxx> > --- > client/tests/kvm/kvm_test_utils.py | 36 ++- > client/tests/kvm/kvm_utils.py | 16 + > client/tests/kvm/kvm_vm.py | 17 + > client/tests/kvm/scripts/allocator.py | 234 +++++++++++++ > client/tests/kvm/tests/ksm_overcommit.py | 559 ++++++++++++++++++++++++++++++ > client/tests/kvm/tests_base.cfg.sample | 23 ++ > 6 files changed, 884 insertions(+), 1 deletions(-) > create mode 100644 client/tests/kvm/scripts/allocator.py > create mode 100644 client/tests/kvm/tests/ksm_overcommit.py > > diff --git a/client/tests/kvm/kvm_test_utils.py b/client/tests/kvm/kvm_test_utils.py > index 02ec0cf..7d96d6e 100644 > --- a/client/tests/kvm/kvm_test_utils.py > +++ b/client/tests/kvm/kvm_test_utils.py > @@ -22,7 +22,8 @@ More specifically: > """ > > import time, os, logging, re, commands > -from autotest_lib.client.common_lib import utils, error > +from autotest_lib.client.common_lib import error > +from autotest_lib.client.bin import utils > import kvm_utils, kvm_vm, kvm_subprocess > > > @@ -203,3 +204,36 @@ def get_time(session, time_command, time_filter_re, time_format): > s = re.findall(time_filter_re, s)[0] > guest_time = time.mktime(time.strptime(s, time_format)) > return (host_time, guest_time) > + > + > +def get_memory_info(lvms): > + """ > + Get memory information from host and guests in format: > + Host: memfree = XXXM; Guests memsh = {XXX,XXX,...} > + > + @params lvms: List of VM objects > + @return: String with memory info report > + """ > + if not isinstance(lvms, list): > + raise error.TestError("Invalid list passed to get_stat: %s " % lvms) > + > + try: > + meminfo = "Host: memfree = " > + meminfo += str(int(utils.freememtotal()) / 1024) + "M; " > + meminfo += "swapfree = " > + mf = int(utils.read_from_meminfo("SwapFree")) / 1024 > + meminfo += str(mf) + "M; " > + except Exception, e: > + raise error.TestFail("Could not fetch host free memory info, " > + "reason: %s" % e) > + > + meminfo += "Guests memsh = {" > + for vm in lvms: > + shm = vm.get_shared_meminfo() > + if shm is None: > + raise error.TestError("Could not get shared meminfo from " > + "VM %s" % vm) > + meminfo += "%dM; " % shm > + meminfo = meminfo[0:-2] + "}" > + > + return meminfo > diff --git a/client/tests/kvm/kvm_utils.py b/client/tests/kvm/kvm_utils.py > index e155951..4565dc1 100644 > --- a/client/tests/kvm/kvm_utils.py > +++ b/client/tests/kvm/kvm_utils.py > @@ -696,6 +696,22 @@ def generate_random_string(length): > return str > > > +def generate_tmp_file_name(file, ext=None, dir='/tmp/'): > + """ > + Returns a temporary file name. The file is not created. > + """ > + while True: > + file_name = (file + '-' + time.strftime("%Y%m%d-%H%M%S-") + > + generate_random_string(4)) > + if ext: > + file_name += '.' + ext > + file_name = os.path.join(dir, file_name) > + if not os.path.exists(file_name): > + break > + > + return file_name > + > + > def format_str_for_message(str): > """ > Format str so that it can be appended to a message. > diff --git a/client/tests/kvm/kvm_vm.py b/client/tests/kvm/kvm_vm.py > index c166ac9..921414d 100755 > --- a/client/tests/kvm/kvm_vm.py > +++ b/client/tests/kvm/kvm_vm.py > @@ -748,6 +748,23 @@ class VM: > return self.process.get_pid() > > > + def get_shared_meminfo(self): > + """ > + Returns the VM's shared memory information. > + > + @return: Shared memory used by VM (MB) > + """ > + if self.is_dead(): > + logging.error("Could not get shared memory info from dead VM.") > + return None > + > + cmd = "cat /proc/%d/statm" % self.params.get('pid_' + self.name) > + shm = int(os.popen(cmd).readline().split()[2]) > + # statm stores informations in pages, translate it to MB > + shm = shm * 4 / 1024 > + return shm > + > + > def remote_login(self, nic_index=0, timeout=10): > """ > Log into the guest via SSH/Telnet/Netcat. > diff --git a/client/tests/kvm/scripts/allocator.py b/client/tests/kvm/scripts/allocator.py > new file mode 100644 > index 0000000..1036893 > --- /dev/null > +++ b/client/tests/kvm/scripts/allocator.py > @@ -0,0 +1,234 @@ > +#!/usr/bin/python > +# -*- coding: utf-8 -*- > +""" > +Auxiliary script used to allocate memory on guests. > + > +@copyright: 2008-2009 Red Hat Inc. > +@author: Jiri Zupka (jzupka@xxxxxxxxxx) > +""" > + > + > +import os, array, sys, struct, random, copy, inspect, tempfile, datetime > + > +PAGE_SIZE = 4096 # machine page size > + > + > +class MemFill(object): > + """ > + Fills guest memory according to certain patterns. > + """ > + def __init__(self, mem, static_value, random_key): > + """ > + Constructor of MemFill class. > + > + @param mem: Amount of test memory in MB. > + @param random_key: Seed of random series used for fill up memory. > + @param static_value: Value used to fill all memory. > + """ > + if (static_value < 0 or static_value > 255): > + print ("FAIL: Initialization static value" > + "can be only in range (0..255)") > + return > + > + self.tmpdp = tempfile.mkdtemp() > + ret_code = os.system("mount -o size=%dM tmpfs %s -t tmpfs" % > + ((mem + 25), self.tmpdp)) > + if ret_code != 0: > + if os.getuid() != 0: > + print ("FAIL: Unable to mount tmpfs " > + "(likely cause: you are not root)") > + else: > + print "FAIL: Unable to mount tmpfs" > + else: > + self.f = tempfile.TemporaryFile(prefix='mem', dir=self.tmpdp) > + self.allocate_by = 'L' > + self.npages = (mem * 1024 * 1024) / PAGE_SIZE > + self.random_key = random_key > + self.static_value = static_value > + print "PASS: Initialization" > + > + > + def __del__(self): > + if os.path.ismount(self.tmpdp): > + self.f.close() > + os.system("umount %s" % (self.tmpdp)) > + > + > + def compare_page(self, original, inmem): > + """ > + Compare pages of memory and print the differences found. > + > + @param original: Data that was expected to be in memory. > + @param inmem: Data in memory. > + """ > + for ip in range(PAGE_SIZE / original.itemsize): > + if (not original[ip] == inmem[ip]): # find which item is wrong > + originalp = array.array("B") > + inmemp = array.array("B") > + originalp.fromstring(original[ip:ip+1].tostring()) > + inmemp.fromstring(inmem[ip:ip+1].tostring()) > + for ib in range(len(originalp)): # find wrong byte in item > + if not (originalp[ib] == inmemp[ib]): > + position = (self.f.tell() - PAGE_SIZE + ip * > + original.itemsize + ib) > + print ("Mem error on position %d wanted 0x%Lx and is " > + "0x%Lx" % (position, originalp[ib], inmemp[ib])) > + > + > + def value_page(self, value): > + """ > + Create page filled by value. > + > + @param value: String we want to fill the page with. > + @return: return array of bytes size PAGE_SIZE. > + """ > + a = array.array("B") > + for i in range(PAGE_SIZE / a.itemsize): > + try: > + a.append(value) > + except: > + print "FAIL: Value can be only in range (0..255)" > + return a > + > + > + def random_page(self, seed): > + """ > + Create page filled by static random series. > + > + @param seed: Seed of random series. > + @return: Static random array series. > + """ > + random.seed(seed) > + a = array.array(self.allocate_by) > + for i in range(PAGE_SIZE / a.itemsize): > + a.append(random.randrange(0, sys.maxint)) > + return a > + > + > + def value_fill(self, value=None): > + """ > + Fill memory page by page, with value generated with value_page. > + > + @param value: Parameter to be passed to value_page. None to just use > + what's on the attribute static_value. > + """ > + self.f.seek(0) > + if value is None: > + value = self.static_value > + page = self.value_page(value) > + for pages in range(self.npages): > + page.tofile(self.f) > + print "PASS: Mem value fill" > + > + > + def value_check(self, value=None): > + """ > + Check memory to see if data is correct. > + > + @param value: Parameter to be passed to value_page. None to just use > + what's on the attribute static_value. > + @return: if data in memory is correct return PASS > + else print some wrong data and return FAIL > + """ > + self.f.seek(0) > + e = 2 > + failure = False > + if value is None: > + value = self.static_value > + page = self.value_page(value) > + for pages in range(self.npages): > + pf = array.array("B") > + pf.fromfile(self.f, PAGE_SIZE / pf.itemsize) > + if not (page == pf): > + failure = True > + self.compare_page(page, pf) > + e = e - 1 > + if e == 0: > + break > + if failure: > + print "FAIL: value verification" > + else: > + print "PASS: value verification" > + > + > + def static_random_fill(self, n_bytes_on_end=PAGE_SIZE): > + """ > + Fill memory by page with static random series with added special value > + on random place in pages. > + > + @param n_bytes_on_end: how many bytes on the end of page can be changed. > + @return: PASS. > + """ > + self.f.seek(0) > + page = self.random_page(self.random_key) > + random.seed(self.random_key) > + p = copy.copy(page) > + > + t_start = datetime.datetime.now() > + for pages in range(self.npages): > + rand = random.randint(((PAGE_SIZE / page.itemsize) - 1) - > + (n_bytes_on_end / page.itemsize), > + (PAGE_SIZE/page.itemsize) - 1) > + p[rand] = pages > + p.tofile(self.f) > + p[rand] = page[rand] > + > + t_end = datetime.datetime.now() > + delta = t_end - t_start > + milisec = delta.microseconds / 1e3 + delta.seconds * 1e3 > + print "PASS: filling duration = %Ld ms" % milisec > + > + > + def static_random_verify(self, n_bytes_on_end=PAGE_SIZE): > + """ > + Check memory to see if it contains correct contents. > + > + @return: if data in memory is correct return PASS > + else print some wrong data and return FAIL. > + """ > + self.f.seek(0) > + e = 2 > + page = self.random_page(self.random_key) > + random.seed(self.random_key) > + p = copy.copy(page) > + failure = False > + for pages in range(self.npages): > + rand = random.randint(((PAGE_SIZE/page.itemsize) - 1) - > + (n_bytes_on_end/page.itemsize), > + (PAGE_SIZE/page.itemsize) - 1) > + p[rand] = pages > + pf = array.array(self.allocate_by) > + pf.fromfile(self.f, PAGE_SIZE / pf.itemsize) > + if not (p == pf): > + failure = True > + self.compare_page(p, pf) > + e = e - 1 > + if e == 0: > + break > + p[rand] = page[rand] > + if failure: > + print "FAIL: Random series verification" > + else: > + print "PASS: Random series verification" > + > + > +def die(): > + """ > + Quit allocator. > + """ > + exit(0) > + > + > +def main(): > + """ > + Main (infinite) loop of allocator. > + """ > + print "PASS: Start" > + end = False > + while not end: > + str = raw_input() > + exec str > + > + > +if __name__ == "__main__": > + main() > diff --git a/client/tests/kvm/tests/ksm_overcommit.py b/client/tests/kvm/tests/ksm_overcommit.py > new file mode 100644 > index 0000000..2dd46c4 > --- /dev/null > +++ b/client/tests/kvm/tests/ksm_overcommit.py > @@ -0,0 +1,559 @@ > +import logging, time, random, string, math, os, tempfile > +from autotest_lib.client.common_lib import error > +from autotest_lib.client.bin import utils > +import kvm_subprocess, kvm_test_utils, kvm_utils, kvm_preprocessing > + > + > +def run_ksm_overcommit(test, params, env): > + """ > + Test how KSM (Kernel Shared Memory) act when more than physical memory is > + used. In second part we also test how KVM handles a situation when the host > + runs out of memory (it is expected to pause the guest system, wait until > + some process returns memory and bring the guest back to life) > + > + @param test: kvm test object. > + @param params: Dictionary with test parameters. > + @param env: Dictionary with the test wnvironment. > + """ > + > + def _start_allocator(vm, session, timeout): > + """ > + Execute allocator.py on a guest, wait until it is initialized. > + > + @param vm: VM object. > + @param session: Remote session to a VM object. > + @param timeout: Timeout that will be used to verify if allocator.py > + started properly. > + """ > + logging.debug("Starting allocator.py on guest %s", vm.name) > + session.sendline("python /tmp/allocator.py") > + (match, data) = session.read_until_last_line_matches(["PASS:", "FAIL:"], > + timeout) > + if match == 1 or match is None: > + raise error.TestFail("Command allocator.py on guest %s failed.\n" > + "return code: %s\n output:\n%s" % > + (vm.name, match, data)) > + > + > + def _execute_allocator(command, vm, session, timeout): > + """ > + Execute a given command on allocator.py main loop, indicating the vm > + the command was executed on. > + > + @param command: Command that will be executed. > + @param vm: VM object. > + @param session: Remote session to VM object. > + @param timeout: Timeout used to verify expected output. > + > + @return: Tuple (match index, data) > + """ > + logging.debug("Executing '%s' on allocator.py loop, vm: %s, timeout: %s", > + command, vm.name, timeout) > + session.sendline(command) > + (match, data) = session.read_until_last_line_matches(["PASS:","FAIL:"], > + timeout) > + if match == 1 or match is None: > + raise error.TestFail("Failed to execute '%s' on allocator.py, " > + "vm: %s, output:\n%s" % > + (command, vm.name, data)) > + return (match, data) > + > + > + def initialize_guests(): > + """ > + Initialize guests (fill their memories with specified patterns). > + """ > + logging.info("Phase 1: filling guest memory pages") > + for session in lsessions: > + vm = lvms[lsessions.index(session)] > + > + logging.debug("Turning off swap on vm %s" % vm.name) > + ret = session.get_command_status("swapoff -a", timeout=300) > + if ret is None or ret: > + raise error.TestFail("Failed to swapoff on VM %s" % vm.name) > + > + # Start the allocator > + _start_allocator(vm, session, 60 * perf_ratio) > + > + # Execute allocator on guests > + for i in range(0, vmsc): > + vm = lvms[i] > + > + a_cmd = "mem = MemFill(%d, %s, %s)" % (ksm_size, skeys[i], dkeys[i]) > + _execute_allocator(a_cmd, vm, lsessions[i], 60 * perf_ratio) > + > + a_cmd = "mem.value_fill(%d)" % skeys[0] > + _execute_allocator(a_cmd, vm, lsessions[i], 120 * perf_ratio) > + > + # Let allocator.py do its job > + # (until shared mem reaches expected value) > + shm = 0 > + i = 0 > + logging.debug("Target shared meminfo for guest %s: %s", vm.name, > + ksm_size) > + while shm < ksm_size: > + if i > 64: > + logging.debug(kvm_test_utils.get_memory_info(lvms)) > + raise error.TestError("SHM didn't merge the memory until " > + "the DL on guest: %s" % vm.name) > + st = ksm_size / 200 * perf_ratio > + logging.debug("Waiting %ds before proceeding..." % st) > + time.sleep(st) > + shm = vm.get_shared_meminfo() > + logging.debug("Shared meminfo for guest %s after " > + "iteration %s: %s", vm.name, i, shm) > + i += 1 > + > + # Keep some reserve > + rt = ksm_size / 200 * perf_ratio > + logging.debug("Waiting %ds before proceeding...", rt) > + time.sleep(rt) > + > + logging.debug(kvm_test_utils.get_memory_info(lvms)) > + logging.info("Phase 1: PASS") > + > + > + def separate_first_guest(): > + """ > + Separate memory of the first guest by generating special random series > + """ > + logging.info("Phase 2: Split the pages on the first guest") > + > + a_cmd = "mem.static_random_fill()" > + (match, data) = _execute_allocator(a_cmd, lvms[0], lsessions[0], > + 120 * perf_ratio) > + > + r_msg = data.splitlines()[-1] > + logging.debug("Return message of static_random_fill: %s", r_msg) > + out = int(r_msg.split()[4]) > + logging.debug("Performance: %dMB * 1000 / %dms = %dMB/s", ksm_size, out, > + (ksm_size * 1000 / out)) > + logging.debug(kvm_test_utils.get_memory_info(lvms)) > + logging.debug("Phase 2: PASS") > + > + > + def split_guest(): > + """ > + Sequential split of pages on guests up to memory limit > + """ > + logging.info("Phase 3a: Sequential split of pages on guests up to " > + "memory limit") > + last_vm = 0 > + session = None > + vm = None > + for i in range(1, vmsc): > + vm = lvms[i] > + session = lsessions[i] > + a_cmd = "mem.static_random_fill()" > + logging.debug("Executing %s on allocator.py loop, vm: %s", > + a_cmd, vm.name) > + session.sendline(a_cmd) > + > + out = "" > + try: > + logging.debug("Watching host memory while filling vm %s memory", > + vm.name) > + while not out.startswith("PASS") and not out.startswith("FAIL"): > + free_mem = int(utils.read_from_meminfo("MemFree")) > + if (ksm_swap): > + free_mem = (free_mem + > + int(utils.read_from_meminfo("SwapFree"))) > + logging.debug("Free memory on host: %d" % (free_mem)) > + > + # We need to keep some memory for python to run. > + if (free_mem < 64000) or (ksm_swap and > + free_mem < (450000 * perf_ratio)): > + vm.send_monitor_cmd('stop') > + for j in range(0, i): > + lvms[j].destroy(gracefully = False) > + time.sleep(20) > + vm.send_monitor_cmd('c') > + logging.debug("Only %s free memory, killing %d guests" % > + (free_mem, (i-1))) > + last_vm = i > + break > + out = session.read_nonblocking(0.1) > + time.sleep(2) > + except OSError, (err): > + logging.debug("Only %s host free memory, killing %d guests" % > + (free_mem, (i - 1))) > + logging.debug("Stopping %s", vm.name) > + vm.send_monitor_cmd('stop') > + for j in range(0, i): > + logging.debug("Destroying %s", lvms[j].name) > + lvms[j].destroy(gracefully = False) > + time.sleep(20) > + vm.send_monitor_cmd('c') > + last_vm = i > + > + if last_vm != 0: > + break > + logging.debug("Memory filled for guest %s" % (vm.name)) > + > + logging.info("Phase 3a: PASS") > + > + logging.info("Phase 3b: Check if memory in max loading guest is right") > + for i in range(last_vm + 1, vmsc): > + lsessions[i].close() > + if i == (vmsc - 1): > + logging.debug(kvm_test_utils.get_memory_info([lvms[i]])) > + logging.debug("Destroying guest %s" % lvms[i].name) > + lvms[i].destroy(gracefully = False) > + > + # Verify last machine with randomly generated memory > + a_cmd = "mem.static_random_verify()" > + _execute_allocator(a_cmd, lvms[last_vm], session, > + (mem / 200 * 50 * perf_ratio)) > + logging.debug(kvm_test_utils.get_memory_info([lvms[last_vm]])) > + > + (status, data) = lsessions[i].get_command_status_output("die()", 20) > + lvms[last_vm].destroy(gracefully = False) > + logging.info("Phase 3b: PASS") > + > + > + def split_parallel(): > + """ > + Parallel page spliting > + """ > + logging.info("Phase 1: parallel page spliting") > + # We have to wait until allocator is finished (it waits 5 seconds to > + # clean the socket > + > + session = lsessions[0] > + vm = lvms[0] > + for i in range(1, max_alloc): > + lsessions.append(kvm_utils.wait_for(vm.remote_login, 360, 0, 2)) > + if not lsessions[i]: > + raise error.TestFail("Could not log into guest %s" % > + vm.name) > + > + ret = session.get_command_status("swapoff -a", timeout=300) > + if ret != 0: > + raise error.TestFail("Failed to turn off swap on %s" % vm.name) > + > + for i in range(0, max_alloc): > + # Start the allocator > + _start_allocator(vm, lsessions[i], 60 * perf_ratio) > + > + logging.info("Phase 1: PASS") > + > + logging.info("Phase 2a: Simultaneous merging") > + logging.debug("Memory used by allocator on guests = %dMB" % > + (ksm_size / max_alloc)) > + > + for i in range(0, max_alloc): > + a_cmd = "mem = MemFill(%d, %s, %s)" % ((ksm_size / max_alloc), > + skeys[i], dkeys[i]) > + _execute_allocator(a_cmd, vm, lsessions[i], 60 * perf_ratio) > + > + a_cmd = "mem.value_fill(%d)" % (skeys[0]) > + _execute_allocator(a_cmd, vm, lsessions[i], 90 * perf_ratio) > + > + # Wait until allocator.py merges the pages (3 * ksm_size / 3) > + shm = 0 > + i = 0 > + logging.debug("Target shared memory size: %s", ksm_size) > + while shm < ksm_size: > + if i > 64: > + logging.debug(kvm_test_utils.get_memory_info(lvms)) > + raise error.TestError("SHM didn't merge the memory until DL") > + wt = ksm_size / 200 * perf_ratio > + logging.debug("Waiting %ds before proceed...", wt) > + time.sleep(wt) > + shm = vm.get_shared_meminfo() > + logging.debug("Shared meminfo after attempt %s: %s", i, shm) > + i += 1 > + > + logging.debug(kvm_test_utils.get_memory_info([vm])) > + logging.info("Phase 2a: PASS") > + > + logging.info("Phase 2b: Simultaneous spliting") > + # Actual splitting > + for i in range(0, max_alloc): > + a_cmd = "mem.static_random_fill()" > + (match, data) = _execute_allocator(a_cmd, vm, lsessions[i], > + 90 * perf_ratio) > + > + data = data.splitlines()[-1] > + logging.debug(data) > + out = int(data.split()[4]) > + logging.debug("Performance: %dMB * 1000 / %dms = %dMB/s" % > + ((ksm_size / max_alloc), out, > + (ksm_size * 1000 / out / max_alloc))) > + logging.debug(kvm_test_utils.get_memory_info([vm])) > + logging.info("Phase 2b: PASS") > + > + logging.info("Phase 2c: Simultaneous verification") > + for i in range(0, max_alloc): > + a_cmd = "mem.static_random_verify()" > + (match, data) = _execute_allocator(a_cmd, vm, lsessions[i], > + (mem / 200 * 50 * perf_ratio)) > + logging.info("Phase 2c: PASS") > + > + logging.info("Phase 2d: Simultaneous merging") > + # Actual splitting > + for i in range(0, max_alloc): > + a_cmd = "mem.value_fill(%d)" % skeys[0] > + (match, data) = _execute_allocator(a_cmd, vm, lsessions[i], > + 120 * perf_ratio) > + logging.debug(kvm_test_utils.get_memory_info([vm])) > + logging.info("Phase 2d: PASS") > + > + logging.info("Phase 2e: Simultaneous verification") > + for i in range(0, max_alloc): > + a_cmd = "mem.value_check(%d)" % skeys[0] > + (match, data) = _execute_allocator(a_cmd, vm, lsessions[i], > + (mem / 200 * 50 * perf_ratio)) > + logging.info("Phase 2e: PASS") > + > + logging.info("Phase 2f: Simultaneous spliting last 96B") > + for i in range(0, max_alloc): > + a_cmd = "mem.static_random_fill(96)" > + (match, data) = _execute_allocator(a_cmd, vm, lsessions[i], > + 60 * perf_ratio) > + > + data = data.splitlines()[-1] > + out = int(data.split()[4]) > + logging.debug("Performance: %dMB * 1000 / %dms = %dMB/s", > + ksm_size/max_alloc, out, > + (ksm_size * 1000 / out / max_alloc)) > + > + logging.debug(kvm_test_utils.get_memory_info([vm])) > + logging.info("Phase 2f: PASS") > + > + logging.info("Phase 2g: Simultaneous verification last 96B") > + for i in range(0, max_alloc): > + a_cmd = "mem.static_random_verify(96)" > + (match, data) = _execute_allocator(a_cmd, vm, lsessions[i], > + (mem / 200 * 50 * perf_ratio)) > + logging.debug(kvm_test_utils.get_memory_info([vm])) > + logging.info("Phase 2g: PASS") > + > + logging.debug("Cleaning up...") > + for i in range(0, max_alloc): > + lsessions[i].get_command_status_output("die()", 20) > + session.close() > + vm.destroy(gracefully = False) > + > + > + # Main test code > + logging.info("Starting phase 0: Initialization") > + # host_reserve: mem reserve kept for the host system to run > + host_reserve = int(params.get("ksm_host_reserve", 512)) > + # guest_reserve: mem reserve kept to avoid guest OS to kill processes > + guest_reserve = int(params.get("ksm_guest_reserve", 1024)) > + logging.debug("Memory reserved for host to run: %d", host_reserve) > + logging.debug("Memory reserved for guest to run: %d", guest_reserve) > + > + max_vms = int(params.get("max_vms", 2)) > + overcommit = float(params.get("ksm_overcommit_ratio", 2.0)) > + max_alloc = int(params.get("ksm_parallel_ratio", 1)) > + > + # vmsc: count of all used VMs > + vmsc = int(overcommit) + 1 > + vmsc = max(vmsc, max_vms) > + > + if (params['ksm_mode'] == "serial"): > + max_alloc = vmsc > + > + host_mem = (int(utils.memtotal()) / 1024 - host_reserve) > + > + ksm_swap = False > + if params.get("ksm_swap") == "yes": > + ksm_swap = True > + > + # Performance ratio > + perf_ratio = params.get("ksm_perf_ratio") > + if perf_ratio: > + perf_ratio = float(perf_ratio) > + else: > + perf_ratio = 1 > + > + if (params['ksm_mode'] == "parallel"): > + vmsc = 1 > + overcommit = 1 > + mem = host_mem > + # 32bit system adjustment > + if not params['image_name'].endswith("64"): > + logging.debug("Probably i386 guest architecture, " > + "max allocator mem = 2G") > + # Guest can have more than 2G but > + # kvm mem + 1MB (allocator itself) can't > + if (host_mem > 3100): > + mem = 3100 > + > + if os.popen("uname -i").readline().startswith("i386"): > + logging.debug("Host is i386 architecture, max guest mem is 2G") > + # Guest system with qemu overhead (64M) can't have more than 2G > + if mem > 3100 - 64: > + mem = 3100 - 64 > + > + else: > + # mem: Memory of the guest systems. Maximum must be less than > + # host's physical ram > + mem = int(overcommit * host_mem / vmsc) > + > + # 32bit system adjustment > + if not params['image_name'].endswith("64"): > + logging.debug("Probably i386 guest architecture, " > + "max allocator mem = 2G") > + # Guest can have more than 2G but > + # kvm mem + 1MB (allocator itself) can't > + if mem - guest_reserve - 1 > 3100: > + vmsc = int(math.ceil((host_mem * overcommit) / > + (3100 + guest_reserve))) > + mem = int(math.floor(host_mem * overcommit / vmsc)) > + > + if os.popen("uname -i").readline().startswith("i386"): > + logging.debug("Host is i386 architecture, max guest mem is 2G") > + # Guest system with qemu overhead (64M) can't have more than 2G > + if mem > 3100 - 64: > + vmsc = int(math.ceil((host_mem * overcommit) / > + (3100 - 64.0))) > + mem = int(math.floor(host_mem * overcommit / vmsc)) > + > + logging.debug("Checking KSM status...") > + ksm_flag = 0 > + for line in os.popen('ksmctl info').readlines(): > + if line.startswith('flags'): > + ksm_flag = int(line.split(' ')[1].split(',')[0]) > + if int(ksm_flag) != 1: > + logging.info("KSM module is not loaded! Trying to load module and " > + "start ksmctl...") > + try: > + utils.run("modprobe ksm") > + utils.run("ksmctl start 5000 100") > + except error.CmdError, e: > + raise error.TestFail("Failed to load KSM: %s" % e) > + logging.debug("KSM module loaded and ksmctl started") > + > + swap = int(utils.read_from_meminfo("SwapTotal")) / 1024 > + > + logging.debug("Overcommit = %f", overcommit) > + logging.debug("True overcommit = %f ", (float(vmsc * mem) / > + float(host_mem))) > + logging.debug("Host memory = %dM", host_mem) > + logging.debug("Guest memory = %dM", mem) > + logging.debug("Using swap = %s", ksm_swap) > + logging.debug("Swap = %dM", swap) > + logging.debug("max_vms = %d", max_vms) > + logging.debug("Count of all used VMs = %d", vmsc) > + logging.debug("Performance_ratio = %f", perf_ratio) > + > + # Generate unique keys for random series > + skeys = [] > + dkeys = [] > + for i in range(0, max(vmsc, max_alloc)): > + key = random.randrange(0, 255) > + while key in skeys: > + key = random.randrange(0, 255) > + skeys.append(key) > + > + key = random.randrange(0, 999) > + while key in dkeys: > + key = random.randrange(0, 999) > + dkeys.append(key) > + > + logging.debug("skeys: %s" % skeys) > + logging.debug("dkeys: %s" % dkeys) > + > + lvms = [] > + lsessions = [] > + > + # As we don't know the number and memory amount of VMs in advance, > + # we need to specify and create them here (FIXME: not a nice thing) > + vm_name = params.get("main_vm") > + params['mem'] = mem > + params['vms'] = vm_name > + # Associate pidfile name > + params['pid_' + vm_name] = kvm_utils.generate_tmp_file_name(vm_name, > + 'pid') > + if not params.get('extra_params'): > + params['extra_params'] = ' ' > + params['extra_params_' + vm_name] = params.get('extra_params') > + params['extra_params_' + vm_name] += (" -pidfile %s" % > + (params.get('pid_' + vm_name))) > + params['extra_params'] = params.get('extra_params_'+vm_name) > + > + # ksm_size: amount of memory used by allocator > + ksm_size = mem - guest_reserve > + logging.debug("Memory used by allocator on guests = %dM" % (ksm_size)) > + > + # Creating the first guest > + kvm_preprocessing.preprocess_vm(test, params, env, vm_name) > + lvms.append(kvm_utils.env_get_vm(env, vm_name)) > + if not lvms[0]: > + raise error.TestError("VM object not found in environment") > + if not lvms[0].is_alive(): > + raise error.TestError("VM seems to be dead; Test requires a living " > + "VM") > + > + logging.debug("Booting first guest %s", lvms[0].name) > + > + lsessions.append(kvm_utils.wait_for(lvms[0].remote_login, 360, 0, 2)) > + if not lsessions[0]: > + raise error.TestFail("Could not log into first guest") > + # Associate vm PID > + try: > + tmp = open(params.get('pid_' + vm_name), 'r') > + params['pid_' + vm_name] = int(tmp.readline()) > + except: > + raise error.TestFail("Could not get PID of %s" % (vm_name)) > + > + # Creating other guest systems > + for i in range(1, vmsc): > + vm_name = "vm" + str(i + 1) > + params['pid_' + vm_name] = kvm_utils.generate_tmp_file_name(vm_name, > + 'pid') > + params['extra_params_' + vm_name] = params.get('extra_params') > + params['extra_params_' + vm_name] += (" -pidfile %s" % > + (params.get('pid_' + vm_name))) > + params['extra_params'] = params.get('extra_params_' + vm_name) > + > + # Last VM is later used to run more allocators simultaneously > + lvms.append(lvms[0].clone(vm_name, params)) > + kvm_utils.env_register_vm(env, vm_name, lvms[i]) > + params['vms'] += " " + vm_name > + > + logging.debug("Booting guest %s" % lvms[i].name) > + if not lvms[i].create(): > + raise error.TestFail("Cannot create VM %s" % lvms[i].name) > + if not lvms[i].is_alive(): > + raise error.TestError("VM %s seems to be dead; Test requires a" > + "living VM" % lvms[i].name) > + > + lsessions.append(kvm_utils.wait_for(lvms[i].remote_login, 360, 0, 2)) > + if not lsessions[i]: > + raise error.TestFail("Could not log into guest %s" % > + lvms[i].name) > + try: > + tmp = open(params.get('pid_' + vm_name), 'r') > + params['pid_' + vm_name] = int(tmp.readline()) > + except: > + raise error.TestFail("Could not get PID of %s" % (vm_name)) > + > + # Let guests rest a little bit :-) > + st = vmsc * 2 * perf_ratio > + logging.debug("Waiting %ds before proceed", st) > + time.sleep(vmsc * 2 * perf_ratio) > + logging.debug(kvm_test_utils.get_memory_info(lvms)) > + > + # Copy allocator.py into guests > + pwd = os.path.join(os.environ['AUTODIR'],'tests/kvm') > + vksmd_src = os.path.join(pwd, "scripts/allocator.py") > + dst_dir = "/tmp" > + for vm in lvms: > + if not vm.copy_files_to(vksmd_src, dst_dir): > + raise error.TestFail("copy_files_to failed %s" % vm.name) > + logging.info("Phase 0: PASS") > + > + if params['ksm_mode'] == "parallel": > + logging.info("Starting KSM test parallel mode") > + split_parallel() > + logging.info("KSM test parallel mode: PASS") > + elif params['ksm_mode'] == "serial": > + logging.info("Starting KSM test serial mode") > + initialize_guests() > + separate_first_guest() > + split_guest() > + logging.info("KSM test serial mode: PASS") > diff --git a/client/tests/kvm/tests_base.cfg.sample b/client/tests/kvm/tests_base.cfg.sample > index e9fdd05..4516ed0 100644 > --- a/client/tests/kvm/tests_base.cfg.sample > +++ b/client/tests/kvm/tests_base.cfg.sample > @@ -255,6 +255,28 @@ variants: > type = physical_resources_check > catch_uuid_cmd = dmidecode | awk -F: '/UUID/ {print $2}' > > + - ksm_overcommit: > + # Don't preprocess any vms as we need to change its params > + vms = '' > + image_snapshot = yes > + kill_vm_gracefully = no > + type = ksm_overcommit > + # Make host use swap (a value of 'no' will turn off host swap) > + ksm_swap = yes > + no hugepages > + # Overcommit of host memmory > + ksm_overcommit_ratio = 3 > + # Max paralel runs machine > + ksm_parallel_ratio = 4 > + # Host memory reserve > + ksm_host_reserve = 512 > + ksm_guest_reserve = 1024 > + variants: > + - ksm_serial > + ksm_mode = "serial" > + - ksm_parallel > + ksm_mode = "parallel" > + > # system_powerdown, system_reset and shutdown *must* be the last ones > # defined (in this order), since the effect of such tests can leave > # the VM on a bad state. > @@ -278,6 +300,7 @@ variants: > kill_vm_gracefully = no > # Do not define test variants below shutdown > > + > # NICs > variants: > - @rtl8139: > -- > 1.6.6.1 > > -- > To unsubscribe from this list: send the line "unsubscribe kvm" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- Lucas ��.n��������+%������w��{.n�����o�^n�r������&��z�ޗ�zf���h���~����������_��+v���)ߣ�m