This patch add tests for testing cpu flags in qemu: a) interface cpu flags tests 1) qemu -cpu ?model 2) qemu -cpu ?dump 3) qemu -cpu ?cpuid b) guest run cpu flags tests 1) Test boot cpu model. 2) Test boot cpu model and additiona/nonstandard model flags. 3) Test boot fail with host unsupported flags. 4) Test boot guest and try flags under load. 5) Test online offline guest CPUs under load. 6) Test migration with additional flags. There is new c program cpuflags-test which is able to test main Intel cpu flags now . There will be extension for test AMD cpuflags etc in next version.. This program ensure to test special instruction provides cpuflags like rdrand etc.. This patch also contain cpuflag stress test in tests dir which can be used by vitr.autotest test. Pull Request: https://github.com/autotest/autotest/pull/109 Signed-off-by: Jiří Župka <jzupka@xxxxxxxxxx> --- client/tests/cpuflags/control | 12 + client/tests/cpuflags/cpuflags.py | 93 +++ client/tests/kvm/tests/cpuflags.py | 605 ++++++++++++++++++++ client/virt/kvm_vm.py | 19 + client/virt/scripts/cpuflags-test/src/Makefile | 112 ++++ client/virt/scripts/cpuflags-test/src/aes.c | 26 + client/virt/scripts/cpuflags-test/src/avx.c | 43 ++ .../virt/scripts/cpuflags-test/src/cpuflags-test.c | 127 ++++ client/virt/scripts/cpuflags-test/src/pclmul.c | 26 + client/virt/scripts/cpuflags-test/src/rdrand.c | 27 + client/virt/scripts/cpuflags-test/src/sse3.c | 28 + client/virt/scripts/cpuflags-test/src/sse4.c | 28 + client/virt/scripts/cpuflags-test/src/ssse3.c | 24 + client/virt/scripts/cpuflags-test/src/stress.c | 73 +++ client/virt/scripts/cpuflags-test/src/tests.h | 54 ++ client/virt/subtests.cfg.sample | 13 + client/virt/virt_utils.py | 47 ++ 17 files changed, 1357 insertions(+), 0 deletions(-) create mode 100644 client/tests/cpuflags/control create mode 100644 client/tests/cpuflags/cpuflags.py create mode 100644 client/tests/kvm/tests/cpuflags.py create mode 100644 client/virt/scripts/cpuflags-test/src/Makefile create mode 100644 client/virt/scripts/cpuflags-test/src/aes.c create mode 100644 client/virt/scripts/cpuflags-test/src/avx.c create mode 100644 client/virt/scripts/cpuflags-test/src/cpuflags-test.c create mode 100644 client/virt/scripts/cpuflags-test/src/pclmul.c create mode 100644 client/virt/scripts/cpuflags-test/src/rdrand.c create mode 100644 client/virt/scripts/cpuflags-test/src/sse3.c create mode 100644 client/virt/scripts/cpuflags-test/src/sse4.c create mode 100644 client/virt/scripts/cpuflags-test/src/ssse3.c create mode 100644 client/virt/scripts/cpuflags-test/src/stress.c create mode 100644 client/virt/scripts/cpuflags-test/src/tests.h diff --git a/client/tests/cpuflags/control b/client/tests/cpuflags/control new file mode 100644 index 0000000..fc00fba --- /dev/null +++ b/client/tests/cpuflags/control @@ -0,0 +1,12 @@ +AUTHOR = "Jiri Zupka <jzupka@xxxxxxxxxx>" +NAME = "Cpuflags" +TIME = "SHORT" +TEST_CATEGORY = "Functional" +TEST_CLASS = "General" +TEST_TYPE = "client" + +DOC = """ +Autotest test for testing main group ofcpu flags functionalitines. +""" + +job.run_test('cpuflags') diff --git a/client/tests/cpuflags/cpuflags.py b/client/tests/cpuflags/cpuflags.py new file mode 100644 index 0000000..a407488 --- /dev/null +++ b/client/tests/cpuflags/cpuflags.py @@ -0,0 +1,93 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +Autotest test for testing main group ofcpu flags functionalitines. + +@copyright: 2011 Red Hat Inc. +@author: Jiří Župka <jzupka@xxxxxxxxxx> +""" +import os, logging + +from autotest_lib.client.bin import test, utils +from autotest_lib.client.common_lib import error +from autotest_lib.client.virt import virt_utils + +class cpuflags(test.test): + """ + Tests the cpuflags functionalities. + """ + version = 1 + + def setup(self, tarball = 'cpuflags_test.tar.bz2'): + def install_cpuflags_test(tarball): + """ + Compile stress test. + + @param vm: virtual machine. + @param dst_dir: Installation path. + """ + scriptdir = os.path.join(self.job.autodir, "virt", "scripts", + "cpuflags-test") + scriptdir_src = os.path.join(scriptdir, "src") + os.mkdir(self.srcdir) + os.chdir(self.srcdir) + utils.system('cp %s %s' % (os.path.join(scriptdir_src, "*"), + self.srcdir)) + utils.make() + utils.system('sync') + + self.job.require_gcc() + install_cpuflags_test(tarball) + + + def run_once(self): + """ + Try to access different resources which are restricted by cgroup. + """ + logging.info('Starting cpuflags testing') + def check_cpuflags_work(flags): + """ + Check which flags work. + + @param vm: Virtual machine. + @param path: Path of cpuflags_test + @param flags: Flags to test. + @return: Tuple (Working, not working, not tested) flags. + """ + pass_Flags = [] + not_tested = [] + not_working = [] + for f in flags: + try: + for tc in virt_utils.kvm_map_flags_to_test[f]: + utils.run("./cpuflags-test --%s" % (tc)) + pass_Flags.append(f) + except error.CmdError: + not_working.append(f) + except KeyError: + not_tested.append(f) + return (pass_Flags, not_working, not_tested) + + + def run_stress(timeout, flags, smp): + """ + Run stress on vm for timeout time. + """ + ret = False + flags = check_cpuflags_work(flags) + try: + utils.run("./cpuflags-test --stress %s%s" % + (smp, virt_utils.kvm_flags_to_stresstests(flags[0])), + timeout) + except error.CmdError: + ret = True + return ret + + + os.chdir(self.srcdir) + run_stress(60, set(map(virt_utils.Flag, virt_utils.get_cpu_flags())), 4) + + + def cleanup(self): + """ Cleanup """ + logging.debug('cpuflags_test cleanup') diff --git a/client/tests/kvm/tests/cpuflags.py b/client/tests/kvm/tests/cpuflags.py new file mode 100644 index 0000000..7bb07b0 --- /dev/null +++ b/client/tests/kvm/tests/cpuflags.py @@ -0,0 +1,605 @@ +import logging, re, random, os, time, socket +from autotest_lib.client.common_lib import error, utils +from autotest_lib.client.virt import kvm_vm +from autotest_lib.client.virt import virt_utils, aexpect +from autotest_lib.client.common_lib.test import Subtest, subtest_nocleanup +from autotest_lib.client.common_lib.test import subtest_fatal + + +def run_cpuflags(test, params, env): + """ + Boot guest with different cpu flags and check if guest work correctly. + + + @param test: kvm test object + + @param params: Dictionary with the test parameters + @param env: Dictionary with test environment. + """ + qemu_binary = virt_utils.get_path('.', params.get("qemu_binary", "qemu")) + + cpuflags_path = os.path.join(test.job.autodir, "virt", "scripts", + "cpuflags-test") + cpuflags_tar = "cpuflags-test.tar.bz2" + cpuflags_src = os.path.join(test.job.autodir, cpuflags_path, "src") + smp = int(params.get("smp", 1)) + + all_host_supported_flags = params.get("all_host_supported_flags", "no") + + mig_timeout = float(params.get("mig_timeout", "3600")) + mig_protocol = params.get("migration_protocol", "tcp") + mig_speed = params.get("mig_speed", "1G") + + + class Hg_flags: + def __init__(self, cpu_model, extra_flags=set([])): + virtual_flags = set(map(virt_utils.Flag, + params.get("guest_spec_flags", "").split())) + self.hw_flags = set(map(virt_utils.Flag, + params.get("host_spec_flags", "").split())) + self.qemu_support_flags = get_all_qemu_flags() + self.host_support_flags = set(map(virt_utils.Flag, + virt_utils.get_cpu_flags())) + self.quest_cpu_model_flags = (get_guest_host_cpuflags(cpu_model) - + virtual_flags) + + self.supported_flags = (self.qemu_support_flags & + self.host_support_flags) + self.cpumodel_unsupport_flags = (self.supported_flags - + self.quest_cpu_model_flags) + + self.host_unsupported_flags = (self.quest_cpu_model_flags - + self.host_support_flags) + + self.all_possible_guest_flags = (self.quest_cpu_model_flags - + self.host_unsupported_flags) + self.all_possible_guest_flags |= self.cpumodel_unsupport_flags + + self.guest_flags = (self.quest_cpu_model_flags - + self.host_unsupported_flags) + self.guest_flags |= extra_flags + + self.host_all_unsupported_flags = set([]) + self.host_all_unsupported_flags |= self.qemu_support_flags + self.host_all_unsupported_flags -= (self.host_support_flags | + virtual_flags) + + + def start_guest_with_cpuflags(cpuflags, smp=None): + """ + Try to boot guest with special cpu flags and try login in to them. + """ + params_b = params.copy() + params_b["cpu_model"] = cpuflags + if smp is not None: + params_b["smp"] = smp + + vm_name = "vm1-cpuflags" + vm = kvm_vm.VM(vm_name, params_b, test.bindir, env['address_cache']) + env.register_vm(vm_name, vm) + vm.create() + vm.verify_alive() + + session = vm.wait_for_login() + + return (vm, session) + + def get_guest_system_cpuflags(vm_session): + """ + Get guest system cpuflags. + + @param vm_session: session to checked vm. + @return: [corespond flags] + """ + flags_re = re.compile(r'^flags\s*:(.*)$', re.MULTILINE) + out = vm_session.cmd_output("cat /proc/cpuinfo") + + flags = flags_re.search(out).groups()[0].split() + return set(map(virt_utils.Flag, flags)) + + + def get_guest_host_cpuflags(cpumodel): + """ + Get cpu flags correspond with cpumodel parameters. + + @param cpumodel: Cpumodel parameter sended to <qemu-kvm-cmd>. + @return: [corespond flags] + """ + cmd = qemu_binary + " -cpu ?dump" + output = utils.run(cmd).stdout + re.escape(cpumodel) + pattern = (".+%s.*\n.*\n +feature_edx .+ \((.*)\)\n +feature_" + "ecx .+ \((.*)\)\n +extfeature_edx .+ \((.*)\)\n +" + "extfeature_ecx .+ \((.*)\)\n" % (cpumodel)) + flags = [] + model = re.search(pattern, output) + if model == None: + raise error.TestFail("Cannot find %s cpu model." % (cpumodel)) + for flag_group in model.groups(): + flags += flag_group.split() + return set(map(virt_utils.Flag, flags)) + + + def get_all_qemu_flags(): + cmd = qemu_binary + " -cpu ?cpuid" + output = utils.run(cmd).stdout + + flags_re = re.compile(r".*\n.*f_edx:(.*)\n.*f_ecx:(.*)\n.*extf_edx:" + "(.*)\n.*extf_ecx:(.*)") + m = flags_re.search(output) + flags = [] + for a in m.groups(): + flags += a.split() + + return set(map(virt_utils.Flag, flags)) + + + def get_flags_full_name(cpu_flag): + """ + Get all name of Flag. + + @param cpu_flag: Flag + @return: all name of Flag. + """ + cpu_flag = virt_utils.Flag(cpu_flag) + for f in get_all_qemu_flags(): + if f == cpu_flag: + return virt_utils.Flag(f) + return [] + + + def parse_qemu_cpucommand(cpumodel): + """ + Parse qemu cpu params. + + @param cpumodel: Cpu model command. + @return: All flags which guest must have. + """ + flags = cpumodel.split(",") + cpumodel = flags[0] + + qemu_model_flag = get_guest_host_cpuflags(cpumodel) + host_support_flag = set(map(virt_utils.Flag, + virt_utils.get_cpu_flags())) + real_flags = qemu_model_flag & host_support_flag + + for f in flags[1:]: + if f[0].startswith("+"): + real_flags |= set([get_flags_full_name(f[1:])]) + if f[0].startswith("-"): + real_flags -= set([get_flags_full_name(f[1:])]) + + return real_flags + + + def get_cpu_models(): + """ + Get all cpu models from qemu. + + @return: cpu models. + """ + cmd = qemu_binary + " -cpu ?" + output = utils.run(cmd).stdout + + cpu_re = re.compile("\w+\s+\[?(\w+)\]?") + return cpu_re.findall(output) + + + def check_cpuflags(cpumodel, vm_session): + """ + Check if vm flags are same like flags select by cpumodel. + + @param cpumodel: params for -cpu param in qemu-kvm + @param vm_session: session to vm to check flags. + + @return: ([excess], [missing]) flags + """ + gf = get_guest_system_cpuflags(vm_session) + rf = parse_qemu_cpucommand(cpumodel) + + logging.debug("Guest flags: %s" % (gf)) + logging.debug("Host flags: %s" % (rf)) + logging.debug("Flags on guest not defined by host: %s" % (gf-rf)) + return rf-gf + + + def disable_cpu(vm_session, cpu, disable=True): + """ + Disable cpu in guest system. + + @param cpu: CPU id to disable. + @param disable: if True disable cpu else enable cpu. + """ + system_cpu_dir = "/sys/devices/system/cpu/" + cpu_online = system_cpu_dir + "cpu%d/online" % (cpu) + cpu_state = vm_session.cmd_output("cat %s" % cpu_online).strip() + if disable and cpu_state == "1": + vm_session.cmd("echo 0 > %s" % cpu_online) + logging.debug("Guest cpu %d is disabled." % cpu) + elif cpu_state == "0": + vm_session.cmd("echo 1 > %s" % cpu_online) + logging.debug("Guest cpu %d is enabled." % cpu) + + + def install_cpuflags_test_on_vm(vm, dst_dir): + """ + Install stress to vm. + + @param vm: virtual machine. + @param dst_dir: Installation path. + """ + session = vm.wait_for_login() + utils.run("cd %s; make tar" % cpuflags_src) + vm.copy_files_to(os.path.join(test.job.autodir, cpuflags_path, + cpuflags_tar), dst_dir) + session.cmd("cd %s; tar -xvjf %s; cd src;" + " make EXTRA_FLAGS='';" % (dst_dir, cpuflags_tar)) + session.close() + + + def check_cpuflags_work(vm, path, flags): + """ + Check which flags work. + + @param vm: Virtual machine. + @param path: Path of cpuflags_test + @param flags: Flags to test. + @return: Tuple (Working, not working, not tested) flags. + """ + pass_Flags = [] + not_tested = [] + not_working = [] + session = vm.wait_for_login() + for f in flags: + try: + for tc in virt_utils.kvm_map_flags_to_test[f]: + session.cmd("%s/src/cpuflags-test --%s" % (path, tc)) + pass_Flags.append(f) + except aexpect.ShellCmdError: + not_working.append(f) + except KeyError: + not_tested.append(f) + return (set(map(virt_utils.Flag, pass_Flags)), + set(map(virt_utils.Flag, not_working)), + set(map(virt_utils.Flag, not_tested))) + + + def run_stress(vm, timeout, guest_flags): + """ + Run stress on vm for timeout time. + """ + ret = False + install_path = "/tmp" + install_cpuflags_test_on_vm(vm, install_path) + flags = check_cpuflags_work(vm, install_path, guest_flags) + dd_session = vm.wait_for_login() + stress_session = vm.wait_for_login() + dd_session.sendline("dd if=/dev/[svh]da of=/tmp/stressblock" + " bs=10MB count=100 &") + try: + stress_session.cmd("%s/src/cpuflags-test --stress %s%s" % + (install_path, smp, + virt_utils.kvm_flags_to_stresstests(flags[0])), + timeout=timeout) + except aexpect.ShellTimeoutError: + ret = True + stress_session.close() + dd_session.close() + return ret + + + def separe_cpu_model(cpu_model): + try: + (cpu_model, _) = cpu_model.split(":") + except ValueError: + cpu_model = cpu_model + return cpu_model + + + def test_qemu_interface(): + """ + 1) <qemu-kvm-cmd> -cpu ?model + 2) <qemu-kvm-cmd> -cpu ?dump + 3) <qemu-kvm-cmd> -cpu ?cpuid + """ + # 1) <qemu-kvm-cmd> -cpu ?model + class test_qemu_cpu_model(Subtest): + @subtest_fatal + @subtest_nocleanup + def test(self): + cpu_models = params.get("cpu_models","core2duo").split() + cmd = qemu_binary + " -cpu ?model" + result = utils.run(cmd) + missing = [] + cpu_models = map(separe_cpu_model,cpu_models) + for cpu_model in cpu_models: + if not cpu_model in result.stdout: + missing.append(cpu_model) + if missing: + raise error.TestFail("CPU models %s are not in output " + "'%s' of command \n%s" % + (missing, cmd, result.stdout)) + + # 2) <qemu-kvm-cmd> -cpu ?dump + class test_qemu_dump(Subtest): + @subtest_nocleanup + def test(self): + cpu_models = params.get("cpu_models","core2duo").split() + cmd = qemu_binary + " -cpu ?dump" + result = utils.run(cmd) + cpu_models = map(separe_cpu_model,cpu_models) + missing = [] + for cpu_model in cpu_models: + if not cpu_model in result.stdout: + missing.append(cpu_model) + if missing: + raise error.TestFail("CPU models %s are not in output " + "'%s' of command \n%s" % + (missing, cmd, result.stdout)) + + # 3) <qemu-kvm-cmd> -cpu ?cpuid + class test_qemu_cpuid(Subtest): + @subtest_nocleanup + def test(self): + cmd = qemu_binary + " -cpu ?cpuid" + result = utils.run(cmd) + if result.stdout is "": + raise error.TestFail("There aren't any cpu Flag in output" + " '%s' of command \n%s" % + (cmd, result.stdout)) + + test_qemu_cpu_model() + test_qemu_dump() + test_qemu_cpuid() + + + def test_qemu_guest(): + """ + 1) boot with cpu_model + 2) migrate with flags + 3) <qemu-kvm-cmd> -cpu model_name,+Flag + 4) fail boot unsupported flags + 5) check guest flags under load cpu, system (dd) + 6) online/offline CPU + """ + cpu_models = params.get("cpu_models","").split() + if not cpu_models: + cpu_models = get_cpu_models() + logging.debug("Founded cpu models %s." % (str(cpu_models))) + + # 1) boot with cpu_model + class test_boot_cpu_model(Subtest): + def test(self, cpu_model): + logging.debug("Run tests with cpu model %s" % (cpu_model)) + flags = Hg_flags(cpu_model, extra_flags) + (self.vm, session) = start_guest_with_cpuflags(cpu_model) + not_enable_flags = (check_cpuflags(cpu_model, session) - + flags.hw_flags) + if not_enable_flags != set([]): + raise error.TestFail("Flags defined by host and supported" + " by host but not on find on guest:" + " %s" % (not_enable_flags)) + + def clean(self): + logging.info("cleanup") + self.vm.destroy(gracefully=False) + + + # 2) success boot with supported flags + class test_boot_cpu_model_and_additional_flags(test_boot_cpu_model): + def test(self, cpu_model, extra_flags): + flags = Hg_flags(cpu_model, extra_flags) + + logging.debug("Cpu mode flags %s." % + str(flags.quest_cpu_model_flags)) + cpuf_model = cpu_model + + if all_host_supported_flags == "yes": + for fadd in flags.cpumodel_unsupport_flags: + cpuf_model += ",+" + fadd + else: + for fadd in extra_flags: + cpuf_model += ",+" + fadd + + for fdel in flags.host_unsupported_flags: + cpuf_model += ",-" + fdel + + if all_host_supported_flags == "yes": + guest_flags = flags.all_possible_guest_flags + else: + guest_flags = flags.guest_flags + + (self.vm, session) = start_guest_with_cpuflags(cpuf_model) + + not_enable_flags = (check_cpuflags(cpuf_model, session) - + flags.hw_flags) + if not_enable_flags != set([]): + logging.error("Model unsupported flags: %s" % + str(flags.cpumodel_unsupport_flags)) + logging.error("Flags defined by host and supported " + "by host but not on find on guest: %s" % + str(not_enable_flags)) + logging.info("Check main instruction sets.") + + install_path = "/tmp" + install_cpuflags_test_on_vm(self.vm, install_path) + + Flags = check_cpuflags_work(self.vm, install_path, + flags.all_possible_guest_flags) + logging.info("Woking CPU flags: %s" % str(Flags[0])) + logging.info("Not working CPU flags: %s" % str(Flags[1])) + logging.warning("Flags works even if not deffined by guest cpu " + "flags: %s" % str(Flags[0] - guest_flags)) + logging.warning("Not tested CPU flags: %s" % str(Flags[2])) + + if Flags[1] & guest_flags: + raise error.TestFail("Some of flags not work: %s" % + (str(Flags[1]))) + + + # 3) fail boot unsupported flags + class test_fail_boot_with_host_unsupported_flags(Subtest): + @subtest_nocleanup + def test(self, cpu_model, extra_flags): + #This is virtual cpu flags which are supported by + #qemu but no with host cpu. + flags = Hg_flags(cpu_model, extra_flags) + + logging.debug("Unsupported flags %s." % + str(flags.host_all_unsupported_flags)) + cpuf_model = cpu_model + ",enforce" + + # Add unsupported flags. + for fadd in flags.host_all_unsupported_flags: + cpuf_model += ",+" + fadd + + cmd = qemu_binary + " -cpu " + cpuf_model + out = None + try: + out = utils.run(cmd, timeout=5, ignore_status=True).stderr + except error.CmdError: + logging.error("Host boot with unsupported flag") + finally: + uns_re = re.compile("^warning:.*flag '(.+)'", re.MULTILINE) + warn_flags = set(map(virt_utils.Flag, uns_re.findall(out))) + fwarn_flags = flags.host_all_unsupported_flags - warn_flags + if fwarn_flags: + raise error.TestFail("Qemu not warn for flags %s." % + str(fwarn_flags)) + + + # 4) check guest flags under load cpu, stress and system (dd) + class test_boot_guest_and_try_flags_under_load(test_boot_cpu_model): + def test(self, cpu_model, extra_flags): + logging.info("Check guest working cpuflags under load" + " cpu and stress and system (dd).") + + flags = Hg_flags(cpu_model, extra_flags) + + logging.debug("Cpu mode flags %s." % + str(flags.quest_cpu_model_flags)) + logging.debug("Added flags %s." % + str(flags.cpumodel_unsupport_flags)) + cpuf_model = cpu_model + + # Add unsupported flags. + for fadd in flags.cpumodel_unsupport_flags: + cpuf_model += ",+" + fadd + + for fdel in flags.host_unsupported_flags: + cpuf_model += ",-" + fdel + + (self.vm, _) = start_guest_with_cpuflags(cpuf_model, smp) + + if (not run_stress(self.vm, 60, flags.guest_flags)): + raise error.TestFail("Stress test ended before" + " end of test.") + + + # 5) Online/offline CPU + class test_online_offline_guest_CPUs(test_boot_cpu_model): + def test(self, cpu_model, extra_flags): + logging.debug("Run tests with cpu model %s." % (cpu_model)) + flags = Hg_flags(cpu_model, extra_flags) + + (self.vm, session) = start_guest_with_cpuflags(cpu_model, smp) + + def encap(timeout): + random.seed() + begin = time.time() + end = begin + if smp > 1: + while end - begin < 60: + cpu = random.randint(1, smp - 1) + if random.randint(0, 1): + disable_cpu(session, cpu, True) + else: + disable_cpu(session, cpu, False) + end = time.time() + return True + else: + logging.warning("For this test is necessary smp > 1.") + return False + timeout = 60 + + test_flags = flags.guest_flags + if all_host_supported_flags == "yes": + test_flags = flags.all_possible_guest_flags + + result = virt_utils.parallel([(encap, [timeout]), + (run_stress, [self.vm, timeout, + test_flags])]) + if not (result[0] and result[1]): + raise error.TestFail("Stress tests failed before" + " end of testing.") + + + # 6) migration test + class test_migration_with_additional_flags(test_boot_cpu_model): + def test(self, cpu_model, extra_flags): + flags = Hg_flags(cpu_model, extra_flags) + + logging.debug("Cpu mode flags %s." % + str(flags.quest_cpu_model_flags)) + logging.debug("Added flags %s." % + str(flags.cpumodel_unsupport_flags)) + cpuf_model = cpu_model + + # Add unsupported flags. + for fadd in flags.cpumodel_unsupport_flags: + cpuf_model += ",+" + fadd + + for fdel in flags.host_unsupported_flags: + cpuf_model += ",-" + fdel + + (self.vm, _) = start_guest_with_cpuflags(cpuf_model, smp) + + install_path = "/tmp" + install_cpuflags_test_on_vm(self.vm, install_path) + flags = check_cpuflags_work(self.vm, install_path, + flags.guest_flags) + dd_session = self.vm.wait_for_login() + stress_session = self.vm.wait_for_login() + + dd_session.sendline("nohup dd if=/dev/[svh]da of=/tmp/" + "stressblock bs=10MB count=100 &") + + stress_session.sendline("nohup %s/src/cpuflags-test --stress" + " %s%s &" % (install_path, smp, + virt_utils.kvm_flags_to_stresstests(flags[0]))) + time.sleep(5) + + self.vm.monitor.migrate_set_speed(mig_speed) + self.vm.migrate(mig_timeout, mig_protocol, offline=False) + + time.sleep(5) + + stress_session.cmd('killall cpuflags-test') + + + + for cpu_model in cpu_models: + try: + (cpu_model, extra_flags) = cpu_model.split(":") + extra_flags = set(map(virt_utils.Flag, extra_flags.split(","))) + except ValueError: + cpu_model = cpu_model + extra_flags = set([]) + test_fail_boot_with_host_unsupported_flags(cpu_model, extra_flags) + test_boot_cpu_model(cpu_model) + test_boot_cpu_model_and_additional_flags(cpu_model, extra_flags) + test_boot_guest_and_try_flags_under_load(cpu_model, extra_flags) + test_online_offline_guest_CPUs(cpu_model, extra_flags) + test_migration_with_additional_flags(cpu_model, extra_flags) + + + try: + Subtest.log_append("<qemu-kvm> interface tests.") + test_qemu_interface() + Subtest.log_append("<qemu-kvm> guests tests.") + test_qemu_guest() + finally: + logging.info("\n\nRESULTS:\n%s \n" % (Subtest.get_text_result())) + + if Subtest.has_failed(): + raise error.TestFail("Some of subtest failed.") diff --git a/client/virt/kvm_vm.py b/client/virt/kvm_vm.py index f68ba22..b1189b6 100644 --- a/client/virt/kvm_vm.py +++ b/client/virt/kvm_vm.py @@ -414,6 +414,19 @@ class VM(virt_vm.BaseVM): else: return "" + def add_cpu_flags(help, cpu_model, flags=None, vendor_id=None): + if has_option(help, 'cpu'): + cmd = " -cpu %s" % cpu_model + + if vendor_id: + cmd += ",vendor=\"%s\"" % vendor_id + if flags: + cmd += ",%s" % flags + + return cmd + else: + return "" + def add_usb(help, usb_id, usb_type, multifunction=False, masterbus=None, firstport=None): cmd = "" @@ -600,6 +613,12 @@ class VM(virt_vm.BaseVM): if smp: qemu_cmd += add_smp(help, smp) + cpu_model = params.get("cpu_model") + if cpu_model: + vendor = params.get("cpu_model_vendor") + flags = params.get("cpu_model_flags") + qemu_cmd += add_cpu_flags(help, cpu_model, vendor, flags) + for cdrom in params.objects("cdroms"): cdrom_params = params.object_params(cdrom) iso = cdrom_params.get("cdrom") diff --git a/client/virt/scripts/cpuflags-test/src/Makefile b/client/virt/scripts/cpuflags-test/src/Makefile new file mode 100644 index 0000000..5b77740 --- /dev/null +++ b/client/virt/scripts/cpuflags-test/src/Makefile @@ -0,0 +1,112 @@ +MKDIR = mkdir -p + +OPTFLAGS=-O3 + +CFLAGS= -m64 ${OPTFLAGS} -std=c99 -pipe \ + -ftree-vectorize -ftree-vectorizer-verbose=0 \ + -ffast-math \ + -fopenmp \ + +CFLAGSAVX= -m64 ${OPTFLAGS} -std=c99 -pipe \ + -ftree-vectorize -ftree-vectorizer-verbose=0 \ + -ffast-math \ + -mavx \ + -fopenmp \ + +CFLAGSSSE4= -m64 ${OPTFLAGS} -std=c99 -pipe \ + -ftree-vectorize -ftree-vectorizer-verbose=0 \ + -ffast-math \ + -msse4 -msse4.1 -msse4.2 \ + -fopenmp \ + +CFLAGSSSSE3= -m64 ${OPTFLAGS} -std=c99 -pipe \ + -ftree-vectorize -ftree-vectorizer-verbose=0 \ + -ffast-math \ + -mssse3 \ + -fopenmp \ + +CFLAGSSSE3= -m64 ${OPTFLAGS} -std=c99 -pipe \ + -ftree-vectorize -ftree-vectorizer-verbose=0 \ + -ffast-math \ + -msse3 \ + -fopenmp \ + +CFLAGSAES= -m64 ${OPTFLAGS} -std=c99 -pipe \ + -ftree-vectorize -ftree-vectorizer-verbose=0 \ + -ffast-math \ + -maes \ + -fopenmp \ + +CFLAGSPCLMUL= -m64 ${OPTFLAGS} -std=c99 -pipe \ + -ftree-vectorize -ftree-vectorizer-verbose=0 \ + -ffast-math \ + -mpclmul \ + -fopenmp \ + +CFLAGSRDRAND= -m64 ${OPTFLAGS} -std=c99 -pipe \ + -ftree-vectorize -ftree-vectorizer-verbose=0 \ + -ffast-math \ + -mrdrnd \ + -fopenmp \ + +CFLAGSSTRESS= -m64 ${OPTFLAGS} -std=c99 -pipe \ + -ftree-vectorize -ftree-vectorizer-verbose=0 \ + -ffast-math \ + $(EXTRA_FLAGS) \ + -fopenmp \ + +CXX=g++ +CC=gcc + +LIBS=-lgomp + +.PHONY: default all cpuflags-test clean + +default:cpuflags-test + +all:cpuflags-test + +cpuflags-test: avx.o sse4.o ssse3.o sse3.o aes.o pclmul.o rdrand.o stress.o + $(CC) $(CFLAGS) $(LIBS) cpuflags-test.c -o cpuflags-test \ + aes.o \ + pclmul.o \ + rdrand.o \ + avx.o \ + sse4.o \ + ssse3.o \ + sse3.o \ + stress.o \ + +aes.o: aes.c + $(CC) $(CFLAGSAES) $(LIBS) -c aes.c + +pclmul.o: pclmul.c + $(CC) $(CFLAGSPCLMUL) $(LIBS) -c pclmul.c + +rdrand.o: rdrand.c + $(CC) $(CFLAGSRDRAND) $(LIBS) -c rdrand.c + +avx.o: avx.c + $(CC) $(CFLAGSAVX) $(LIBS) -c avx.c + +sse4.o: sse4.c + $(CC) $(CFLAGSSSE4) $(LIBS) -c sse4.c + +ssse3.o: ssse3.c + $(CC) $(CFLAGSSSSE3) $(LIBS) -c ssse3.c + +sse3.o: sse3.c + $(CC) $(CFLAGSSSE3) $(LIBS) -c sse3.c + +stress.o: stress.c + $(CC) $(CFLAGSSTRESS) $(LIBS) -c stress.c + +ARCHIVE= cpuflags-test + +tar: clean + tar cf - ../src | bzip2 -9 > ../$(ARCHIVE).tar.bz2 + +clean: + rm -f *~ + rm -f *.o + rm -f cpuflags-test diff --git a/client/virt/scripts/cpuflags-test/src/aes.c b/client/virt/scripts/cpuflags-test/src/aes.c new file mode 100644 index 0000000..b8dc5cc --- /dev/null +++ b/client/virt/scripts/cpuflags-test/src/aes.c @@ -0,0 +1,26 @@ +/* + * aes.c + * + * Created on: Nov 29, 2011 + * Author: jzupka + */ + +#include "tests.h" + +#ifdef __AES__ +void aes(){ + __ma128i v1; + __ma128i v2; + for (int i = 1;i >= 0; i--){ + v1.ui64[i] = 3; + v2.ui64[i] = 3; + } + __ma128i v3; + v3.i = _mm_aesdeclast_si128(v1.i, v2.i); + printf("[%d %d %d]\n",v1.ui64[0],v2.ui64[0],v3.ui64[0]); +} +#else +void aes(){ + printf("AES is not supported."); +} +#endif diff --git a/client/virt/scripts/cpuflags-test/src/avx.c b/client/virt/scripts/cpuflags-test/src/avx.c new file mode 100644 index 0000000..bf06929 --- /dev/null +++ b/client/virt/scripts/cpuflags-test/src/avx.c @@ -0,0 +1,43 @@ +/* + * avx.c + * + * Created on: Nov 29, 2011 + * Author: jzupka + */ +#include "tests.h" + +#ifdef __AVX__ + +typedef union __attribute__ ((aligned(32))){ + __m256 v; + float f32[8]; +} __mar256; + + +void avx(){ + __mar256 a,b; + + __m256 ymm0; + __m256 ymm1; + + for (int i = 0;i < 8;i++){ + a.f32[i] = (float)i; + b.f32[i] = (float)i*10; + } + + ymm0 = _mm256_load_ps(a.f32); + ymm1 = _mm256_load_ps(b.f32); + __mar256 ymm3; + ymm3.v = _mm256_sub_ps(ymm0,ymm1); + _mm256_store_ps(b.f32, ymm3.v ); + for (int i = 0;i < 8; i++){ + printf("[%f]\n", b.f32[i]); + } +} + +#endif +#ifndef __AVX__ +void avx(){ + printf("AVX is not supported."); +} +#endif diff --git a/client/virt/scripts/cpuflags-test/src/cpuflags-test.c b/client/virt/scripts/cpuflags-test/src/cpuflags-test.c new file mode 100644 index 0000000..483561c --- /dev/null +++ b/client/virt/scripts/cpuflags-test/src/cpuflags-test.c @@ -0,0 +1,127 @@ +#include <getopt.h> +#include <string.h> +#include "tests.h" + + +void print_help(){ + printf( + " --sse4 test sse4 instruction.\n" + " --ssse3 test ssse3 instruction.\n" + " --avx test avx instruction.\n" + " --aes test aes instruction.\n" + " --pclmul test carry less multiplication.\n" + " --rdrand test rdrand instruction.\n" + " --stress n_cpus,avx,aes start stress on n_cpus.and cpuflags\n"); +} + + +inst parse_Inst(char * optarg){ + inst i; + memset(&i, 0, sizeof(i)); + char * pch; + + pch = strtok (optarg,","); + printf("%s\n",pch); + i.num_threads = atoi(pch); + while (pch != NULL) + { + printf ("%s\n",pch); + if (strcmp(pch,"sse3") == 0){ + i.sse3 = 1; + } + else if(strcmp(pch,"ssse3") == 0){ + i.ssse3 = 1; + } + else if(strcmp(pch,"sse4") == 0){ + i.sse4 = 1; + } + else if(strcmp(pch,"avx") == 0){ + i.avx = 1; + } + else if(strcmp(pch,"aes") == 0){ + i.aes = 1; + } + else if(strcmp(pch,"pclmul") == 0){ + i.pclmul = 1; + } + else if(strcmp(pch,"rdrand") == 0){ + i.rdrand = 1; + } + pch = strtok (NULL, ","); + } + return i; +} + +int main(int argc, char **argv) { + int c; + int digit_optind = 0; + int opt_count = 0; + + while (1) { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + {{ "sse3", no_argument, 0, 0 }, + { "ssse3", no_argument, 0, 0 }, + { "sse4", no_argument, 0, 0 }, + { "avx", no_argument, 0, 0 }, + { "aes", no_argument, 0, 0 }, + { "pclmul", no_argument, 0, 0 }, + { "rdrand", no_argument, 0, 0 }, + { "stress", required_argument, 0, 0 }, + { 0, 0, 0, 0}}; + + c = getopt_long(argc, argv, "", long_options, &option_index); + if (c == -1){ + if (!opt_count) + print_help(); + break; + } + + switch (c) { + case 0: + printf("option %s", long_options[option_index].name); + if (optarg) + printf(" with arg %s", optarg); + printf("\n"); + switch (option_index) { + case 0: + sse3(); + break; + case 1: + ssse3(); + break; + case 2: + sse4(); + break; + case 3: + avx(); + break; + case 4: + aes(); + break; + case 5: + pclmul(); + break; + case 6: + rdrand(); + break; + case 7: + stress(parse_Inst(optarg)); + break; + } + printf("\n"); + break; + + case '?': + print_help(); + break; + + default: + printf("?? getopt returned character code 0%o ??\n", c); + break; + } + opt_count += 1; + } + exit(0); +} diff --git a/client/virt/scripts/cpuflags-test/src/pclmul.c b/client/virt/scripts/cpuflags-test/src/pclmul.c new file mode 100644 index 0000000..3387a17 --- /dev/null +++ b/client/virt/scripts/cpuflags-test/src/pclmul.c @@ -0,0 +1,26 @@ +/* + * pcmul.c + * + * Created on: Nov 29, 2011 + * Author: jzupka + */ + +#include "tests.h" + +#ifdef __PCLMUL__ +void pclmul(){ + __ma128i v1; + __ma128i v2; + for (int i = 1;i >= 0; i--){ + v1.ui64[i] = 3; + v2.ui64[i] = 3; + } + __ma128i v3; + v3.i = _mm_clmulepi64_si128(v1.i, v2.i, 0); + printf("[%d %d %d]\n",v1.ui64[0],v2.ui64[0],v3.ui64[0]); +} +#else +void pclmul(){ + printf("PCMUL is not supported."); +} +#endif diff --git a/client/virt/scripts/cpuflags-test/src/rdrand.c b/client/virt/scripts/cpuflags-test/src/rdrand.c new file mode 100644 index 0000000..f9d1b76 --- /dev/null +++ b/client/virt/scripts/cpuflags-test/src/rdrand.c @@ -0,0 +1,27 @@ +/* + * rdrand.c + * + * Created on: Nov 29, 2011 + * Author: jzupka + */ + +#include "tests.h" + +#ifdef __RDRND__ +void rdrand() +{ + int val, num=1; + while (num--) { + __asm volatile("2:"); + __asm volatile(".byte 0x0f,0xc7,0xf0"); + __asm volatile("jc 4f; loop 2b"); + __asm volatile("4:"); + __asm volatile("movl %%eax,%0" : "=m"(val)); + printf("Random is %d\n",val); + } +} +#else +void rdrand(){ + printf("RDRAND is not supported."); +} +#endif diff --git a/client/virt/scripts/cpuflags-test/src/sse3.c b/client/virt/scripts/cpuflags-test/src/sse3.c new file mode 100644 index 0000000..18d2643 --- /dev/null +++ b/client/virt/scripts/cpuflags-test/src/sse3.c @@ -0,0 +1,28 @@ +/* + * sse3.c + * + * Created on: Nov 29, 2011 + * Author: jzupka + */ + + +#include "tests.h" + +#ifdef __SSE3__ +void sse3(){ + __ma128f v1; + __ma128f v2; + for (int i = 4;i >= 0; i--){ + v1.f32[i] = -i*5.1; + v2.f32[i] = i*10.1; + } + __ma128f vo; + vo.f = _mm_addsub_ps(v1.f,v2.f); + printf("[%f]\n", vo.f32[3]); +} +#else +void sse3(){ + printf("SSE3 is not supported."); +} +#endif + diff --git a/client/virt/scripts/cpuflags-test/src/sse4.c b/client/virt/scripts/cpuflags-test/src/sse4.c new file mode 100644 index 0000000..f9b60fb --- /dev/null +++ b/client/virt/scripts/cpuflags-test/src/sse4.c @@ -0,0 +1,28 @@ +/* + * sse4.c + * + * Created on: Nov 29, 2011 + * Author: jzupka + */ + +#include "tests.h" + +#if (defined __SSE4_1__ || defined __SSE4_2__) +void sse4(){ + __ma128i v1; + __ma128i v2; + for (int i = 16;i >= 0; i--){ + v1.ui8[i] = i; + v2.ui8[i] = 16-i; + } + __ma128i v3; + v3.i = _mm_max_epi8(v1.i,v2.i); + for (int i = 15;i >= 0; i--){ + printf("max[%d]\n",v3.ui8[i]); + } +} +#else +void sse4(){ + printf("SSE4 is not supported."); +} +#endif diff --git a/client/virt/scripts/cpuflags-test/src/ssse3.c b/client/virt/scripts/cpuflags-test/src/ssse3.c new file mode 100644 index 0000000..8372f43 --- /dev/null +++ b/client/virt/scripts/cpuflags-test/src/ssse3.c @@ -0,0 +1,24 @@ +/* + * ssse3.c + * + * Created on: Nov 29, 2011 + * Author: jzupka + */ + +#include "tests.h" + +#ifdef __SSSE3__ +void ssse3(){ + __ma128i v1; + for (int i = 16;i >= 0; i--){ + v1.ui8[i] = -i; + } + __ma128i vo; + vo.i = _mm_abs_epi8(v1.i); + printf("[%d]\n", vo.ui8[4]); +} +#else +void ssse3(){ + printf("SSSE3 is not supported."); +} +#endif diff --git a/client/virt/scripts/cpuflags-test/src/stress.c b/client/virt/scripts/cpuflags-test/src/stress.c new file mode 100644 index 0000000..cad505b --- /dev/null +++ b/client/virt/scripts/cpuflags-test/src/stress.c @@ -0,0 +1,73 @@ +/* + * stress.c + * + * Created on: Nov 29, 2011 + * Author: jzupka + */ + +#include "tests.h" + +#define size (40000000) + +void AddTwo(float *aa, float *bb, int num_threads) { + { + for (int j = 0; j < 4; j++) { + #pragma omp parallel for + for (int i = 0; i < size; i++) { + aa[i] = bb[i] * 100.0f + 2.0f / bb[i]; + } + } + + int *a = malloc(sizeof(int) * 4096); + + #pragma omp parallel for + for (int i = 0; i < 4096; i++){ + a[i] = (int)aa[i]; + } + + int sum = 0; + #pragma omp parallel for reduction(+:sum) + for (int i = 0; i < 2048; i++){ + sum += a[2*i] & a[2*i+1]; + } + printf("%d\n",sum); + free(a); + } +} + + +void stress(inst in) { + + // arrays must be aligned by 16 + float *a = malloc(sizeof(float)*size); + float *b = malloc(sizeof(float)*size); + // define two arrays + for (int i = 0; i < size; i++) { + b[i] = rand(); + } + omp_set_num_threads(in.num_threads); + #pragma omp parallel + while (1){ + AddTwo(a, b, in.num_threads); // call AddTwo function} + if (in.avx) + avx(); + if (in.sse4) + sse4(); + if (in.sse3) + sse3(); + if (in.ssse3) + ssse3(); + if (in.aes) + aes(); + if (in.pclmul) + pclmul(); + if (in.rdrand) + rdrand(); + } + + int r = rand()%size; + printf("rand a[%d]=%f\n",r ,a[r]); + + free(a); + free(b); +} diff --git a/client/virt/scripts/cpuflags-test/src/tests.h b/client/virt/scripts/cpuflags-test/src/tests.h new file mode 100644 index 0000000..a009923 --- /dev/null +++ b/client/virt/scripts/cpuflags-test/src/tests.h @@ -0,0 +1,54 @@ +/* + * test.h + * + * Created on: Nov 29, 2011 + * Author: jzupka + */ + +#ifndef TEST_H_ +#define TEST_H_ + +#include <stdio.h> +#include <stdlib.h> +#include <immintrin.h> +#include <stdint.h> +#include <omp.h> + +typedef struct{ + int num_threads; + char sse3; + char ssse3; + char sse4; + char avx; + char aes; + char pclmul; + char rdrand; +} inst; + +typedef uint16_t auint16_t __attribute__ ((aligned(16))); + +typedef union __attribute__ ((aligned(16))){ + __m128i i; + uint64_t ui64[2]; + uint8_t ui8[16]; +} __ma128i; + +typedef union __attribute__ ((aligned(32))){ + __m128 f; + __m128d d; + float f32[4]; + double d64[2]; +} __ma128f; + +void aes(); +void pclmul(); +void rdrand(); + +void avx(); +void sse4(); +void sse3(); +void ssse3(); +void stress(inst in); + + +#endif /* TEST_H_ */ diff --git a/client/virt/subtests.cfg.sample b/client/virt/subtests.cfg.sample index d1ed5f2..2f45562 100644 --- a/client/virt/subtests.cfg.sample +++ b/client/virt/subtests.cfg.sample @@ -468,6 +468,8 @@ variants: test_control_file = flail.control - systemtap: test_control_file = systemtap.control + - cpuflags-stress: + test_control_file = cpuflags.control - qemu_img_commit: install setup image_copy unattended_install.cdrom type = qemu_img @@ -1369,6 +1371,17 @@ variants: dd_timeout = 900 check_cmd_timeout = 900 + - cpuflags: + type = cpuflags + extra_params += " -snapshot" + #Disable all unnecessary vms. + vms = "" + #Try to start guest with all flags which are supported by host. + all_host_supported_flags = "no" + cpu_models = "core2duo:avx" + guest_spec_flags = "fxsr_opt hypervisor ds pdpe1gb osxsave svm" + host_spec_flags = "pbe tm ds_cpl monitor acpi dtes64 ht tm2 xtpr est pdcm smx" + - cpu_hotplug_test: type = cpu_hotplug cpu_hotplug_timeout = 600 diff --git a/client/virt/virt_utils.py b/client/virt/virt_utils.py index 6b66e83..d1acb97 100644 --- a/client/virt/virt_utils.py +++ b/client/virt/virt_utils.py @@ -1290,6 +1290,53 @@ def get_vendor_from_pci_id(pci_id): return re.sub(":", " ", commands.getoutput(cmd)) +class Flag(str): + """ + Class for easy merge cpuflags. + """ + def __init__(self, *args, **kwargs): + super(Flag, self).__init__( *args, **kwargs) + + def __eq__(self, other): + s = set(self.split("|")) + o = set(other.split("|")) + if s & o: + return True + else: + return False + + def __hash__(self, *args, **kwargs): + return 0 + + +kvm_map_flags_to_test = { + Flag('avx') :set(['avx']), + Flag('sse3') :set(['sse3']), + Flag('ssse3') :set(['ssse3']), + Flag('sse4.1|sse4_1|sse4.2|sse4_2'):set(['sse4']), + Flag('aes') :set(['aes','pclmul']), + Flag('pclmuldq') :set(['pclmul']), + Flag('pclmulqdq') :set(['pclmul']), + Flag('rdrand') :set(['rdrand']), + } + + +def kvm_flags_to_stresstests(flags): + """ + Covert [cpu flags] to [tests] + + @param cpuflags: list of cpuflags + @return: Return tests like string. + """ + tests = set([]) + for f in flags: + tests |= kvm_map_flags_to_test[f] + param = "" + for f in tests: + param += ","+f + return param + + def get_cpu_flags(): """ Returns a list of the CPU flags -- 1.7.7.4 -- 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