* [dts] [PATCH V2 0/4] power_bidirection_channel: upload automation script and test plan @ 2020-01-16 1:56 yufengmx 2020-01-16 1:56 ` [dts] [PATCH V2 1/4] tests/power_bidirection_channel: vm config file yufengmx ` (4 more replies) 0 siblings, 5 replies; 10+ messages in thread From: yufengmx @ 2020-01-16 1:56 UTC (permalink / raw) To: dts, lei.a.yao; +Cc: yufengmx upload automation script and test plan v2: - remove pf passthrough. yufengmx (4): tests/power_bidirection_channel: vm config file tests/power_bidirection_channel: upload automation script test_plans/power_bidirection_channel: add test plan index test_plans/power_bidirection_channel: upload test plan conf/power_bidirection_channel.cfg | 30 + test_plans/index.rst | 1 + .../power_bidirection_channel_test_plan.rst | 173 +++++ tests/TestSuite_power_bidirection_channel.py | 590 ++++++++++++++++++ 4 files changed, 794 insertions(+) create mode 100644 conf/power_bidirection_channel.cfg create mode 100644 test_plans/power_bidirection_channel_test_plan.rst create mode 100644 tests/TestSuite_power_bidirection_channel.py -- 2.21.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [dts] [PATCH V2 1/4] tests/power_bidirection_channel: vm config file 2020-01-16 1:56 [dts] [PATCH V2 0/4] power_bidirection_channel: upload automation script and test plan yufengmx @ 2020-01-16 1:56 ` yufengmx 2020-01-16 2:27 ` Yao, Lei A 2020-01-16 1:56 ` [dts] [PATCH V2 2/4] tests/power_bidirection_channel: upload automation script yufengmx ` (3 subsequent siblings) 4 siblings, 1 reply; 10+ messages in thread From: yufengmx @ 2020-01-16 1:56 UTC (permalink / raw) To: dts, lei.a.yao; +Cc: yufengmx upload vm config file. Signed-off-by: yufengmx <yufengx.mo@intel.com> --- conf/power_bidirection_channel.cfg | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 conf/power_bidirection_channel.cfg diff --git a/conf/power_bidirection_channel.cfg b/conf/power_bidirection_channel.cfg new file mode 100644 index 0000000..bb13bdf --- /dev/null +++ b/conf/power_bidirection_channel.cfg @@ -0,0 +1,30 @@ +# libvirtd options: +# [VM name] section value is the name for VM +# cpu # hard code type to host-passthrough +# number: number of vcpus +# cpupin: host cpu list +# mem +# size: 4096 +# disk +# file: absolute path to disk image +# type: disk image format +# login +# user: user name to login into VM +# password: passwork to login into VM +# device +# pf_idx: pass-through device index of DUT ports +# guestpci: hardcode value of guest pci address +# virtio_serial_channel +# path: virtio unix socket absolute path +# name: virtio serial name in VM + +# vm configuration for vm power management case +[vm0] +cpu = + number=8,cpupin=4 5 6 7 8 9 10 11; +mem = + size=8196; +disk = + file=/home/vms/fedora27.img,opt_format=qcow2; +login = + user=root,password=tester; -- 2.21.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [dts] [PATCH V2 1/4] tests/power_bidirection_channel: vm config file 2020-01-16 1:56 ` [dts] [PATCH V2 1/4] tests/power_bidirection_channel: vm config file yufengmx @ 2020-01-16 2:27 ` Yao, Lei A 0 siblings, 0 replies; 10+ messages in thread From: Yao, Lei A @ 2020-01-16 2:27 UTC (permalink / raw) To: Mo, YufengX, dts > -----Original Message----- > From: Mo, YufengX <yufengx.mo@intel.com> > Sent: Thursday, January 16, 2020 9:56 AM > To: dts@dpdk.org; Yao, Lei A <lei.a.yao@intel.com> > Cc: Mo, YufengX <yufengx.mo@intel.com> > Subject: [dts][PATCH V2 1/4] tests/power_bidirection_channel: vm config > file > > > upload vm config file. > > Signed-off-by: yufengmx <yufengx.mo@intel.com> Acked-by: Lei Yao <lei.a.yao@intel.com> > --- > conf/power_bidirection_channel.cfg | 30 > ++++++++++++++++++++++++++++++ > 1 file changed, 30 insertions(+) > create mode 100644 conf/power_bidirection_channel.cfg > > diff --git a/conf/power_bidirection_channel.cfg > b/conf/power_bidirection_channel.cfg > new file mode 100644 > index 0000000..bb13bdf > --- /dev/null > +++ b/conf/power_bidirection_channel.cfg > @@ -0,0 +1,30 @@ > +# libvirtd options: > +# [VM name] section value is the name for VM > +# cpu # hard code type to host-passthrough > +# number: number of vcpus > +# cpupin: host cpu list > +# mem > +# size: 4096 > +# disk > +# file: absolute path to disk image > +# type: disk image format > +# login > +# user: user name to login into VM > +# password: passwork to login into VM > +# device > +# pf_idx: pass-through device index of DUT ports > +# guestpci: hardcode value of guest pci address > +# virtio_serial_channel > +# path: virtio unix socket absolute path > +# name: virtio serial name in VM > + > +# vm configuration for vm power management case [vm0] cpu = > + number=8,cpupin=4 5 6 7 8 9 10 11; > +mem = > + size=8196; > +disk = > + file=/home/vms/fedora27.img,opt_format=qcow2; > +login = > + user=root,password=tester; > -- > 2.21.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [dts] [PATCH V2 2/4] tests/power_bidirection_channel: upload automation script 2020-01-16 1:56 [dts] [PATCH V2 0/4] power_bidirection_channel: upload automation script and test plan yufengmx 2020-01-16 1:56 ` [dts] [PATCH V2 1/4] tests/power_bidirection_channel: vm config file yufengmx @ 2020-01-16 1:56 ` yufengmx 2020-01-16 2:27 ` Yao, Lei A 2020-01-16 1:56 ` [dts] [PATCH V2 3/4] test_plans/power_bidirection_channel: add test plan index yufengmx ` (2 subsequent siblings) 4 siblings, 1 reply; 10+ messages in thread From: yufengmx @ 2020-01-16 1:56 UTC (permalink / raw) To: dts, lei.a.yao; +Cc: yufengmx upload automation script. Signed-off-by: yufengmx <yufengx.mo@intel.com> --- tests/TestSuite_power_bidirection_channel.py | 590 +++++++++++++++++++ 1 file changed, 590 insertions(+) create mode 100644 tests/TestSuite_power_bidirection_channel.py diff --git a/tests/TestSuite_power_bidirection_channel.py b/tests/TestSuite_power_bidirection_channel.py new file mode 100644 index 0000000..ed82fda --- /dev/null +++ b/tests/TestSuite_power_bidirection_channel.py @@ -0,0 +1,590 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2020 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +DPDK Test suite. +virtual power manager test suite. +""" +import os +import time +import traceback + +from utils import create_mask as dts_create_mask +from qemu_libvirt import LibvirtKvm +from exception import VerifyFailure +from test_case import TestCase + + +class TestPowerBidirectionChannel(TestCase): + # temporary file directory + output_path = '/tmp' + + @property + def target_dir(self): + # get absolute directory of target source code + target_dir = '/root' + self.dut.base_dir[1:] \ + if self.dut.base_dir.startswith('~') else \ + self.dut.base_dir + return target_dir + + def get_cores_mask(self, config='all'): + ports_socket = self.dut.get_numa_id(self.dut.get_ports()[0]) + mask = dts_create_mask( + self.dut.get_core_list(config, socket=ports_socket)) + return mask + + def prepare_binary(self, name, host_crb=None): + _host_crb = host_crb if host_crb else self.dut + example_dir = "examples/" + name + out = _host_crb.build_dpdk_apps('./' + example_dir) + self.verify("Error" not in out, "Compilation error") + self.verify("No such" not in out, "Compilation error") + binary_dir = os.path.join(self.target_dir, example_dir, 'build') + cmd = ["ls -F {0} | grep '*'".format(binary_dir), '# ', 5] + exec_file = self.execute_cmds(cmd, name=_host_crb.session.name) + binary_file = os.path.join(binary_dir, exec_file[:-1]) + return binary_file + + def add_console(self, session): + self.ext_con[session.name] = [ + session.send_expect, + session.session.get_output_all] + + def get_console(self, name): + default_con_table = { + self.dut.session.name: [ + self.dut.send_expect, + self.dut.get_session_output], + self.dut.alt_session.name: [ + self.dut.alt_session.send_expect, + self.dut.alt_session.session.get_output_all]} + if name not in default_con_table: + return self.ext_con.get(name) or [None, None] + else: + return default_con_table.get(name) + + def execute_cmds(self, cmds, name='dut'): + console, msg_pipe = self.get_console(name) + if len(cmds) == 0: + return + if isinstance(cmds, (str, unicode)): + cmds = [cmds, '# ', 5] + if not isinstance(cmds[0], list): + cmds = [cmds] + outputs = [] if len(cmds) > 1 else '' + for item in cmds: + expected_items = item[1] + if expected_items and isinstance(expected_items, (list, tuple)): + check_output = True + expected_str = expected_items[0] or '# ' + else: + check_output = False + expected_str = expected_items or '# ' + + try: + if len(item) == 3: + timeout = int(item[2]) + output = console(item[0], expected_str, timeout) + output = msg_pipe() if not output else output + else: + output = console(item[0], expected_str) + output = msg_pipe() if not output else output + except Exception as e: + msg = "execute '{0}' timeout".format(item[0]) + raise Exception(msg) + time.sleep(1) + if len(cmds) > 1: + outputs.append(output) + else: + outputs = output + + return outputs + + def d_con(self, cmds): + return self.execute_cmds(cmds, name=self.dut.session.name) + + def d_a_con(self, cmds): + return self.execute_cmds(cmds, name=self.dut.alt_session.name) + + def vm_con(self, cmds): + return self.execute_cmds(cmds, name=self.vm_dut.session.name) + + def vm_g_con(self, cmds): + return self.execute_cmds(cmds, name=self.guest_con_name) + + def get_sys_power_driver(self): + drv_file = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver" + output = self.d_a_con('cat ' + drv_file) + if not output: + msg = 'unknown power driver' + self.verify(False, msg) + drv_name = output.splitlines()[0].strip() + return drv_name + + @property + def is_support_pbf(self): + # check if cpu support bpf feature + cpu_attr = r'/sys/devices/system/cpu/cpu0/cpufreq/base_frequency' + cmd = "ls {0}".format(cpu_attr) + self.d_a_con(cmd) + cmd = "echo $?" + output = self.d_a_con(cmd) + ret = True if output == "0" else False + return ret + + def get_all_cpu_attrs(self): + ''' + get all cpus' base_frequency value, if not support pbf, set all to 0 + ''' + if not self.is_support_pbf: + cpu_topos = self.dut.get_all_cores() + _base_freqs_info = {} + for index, _ in enumerate(cpu_topos): + _base_freqs_info[index] = 0 + return _base_freqs_info + # if cpu support high priority core + key_values = ['base_frequency', + 'cpuinfo_max_freq', + 'cpuinfo_min_freq'] + freq = r'/sys/devices/system/cpu/cpu{0}/cpufreq/{1}'.format + # use dut alt session to get dut platform cpu base frequency attribute + cpu_topos = self.dut.get_all_cores() + cpu_info = {} + for cpu_topo in cpu_topos: + cpu_id = int(cpu_topo['thread']) + cpu_info[cpu_id] = {} + cpu_info[cpu_id]['socket'] = cpu_topo['socket'] + cpu_info[cpu_id]['core'] = cpu_topo['core'] + + for key_value in key_values: + cmds = [] + for cpu_id in sorted(cpu_info.keys()): + cmds.append('cat {0}'.format(freq(cpu_id, key_value))) + output = self.d_a_con(';'.join(cmds)) + freqs = [int(item) for item in output.splitlines()] + for index, cpu_id in enumerate(sorted(cpu_info.keys())): + cpu_info[cpu_id][key_value] = freqs[index] + + # get high priority core and normal core + base_freqs_info = {} + for core_index, value in cpu_info.iteritems(): + base_frequency = value.get('base_frequency') + base_freqs_info.setdefault(base_frequency, []).append(core_index) + base_freqs = base_freqs_info.keys() + # cpu should have high priority core and normal core + # high priority core frequency is higher than normal core frequency + if len(base_freqs) <= 1 or \ + not all([len(value) for value in base_freqs_info.values()]): + msg = 'current cpu has no high priority core' + raise Exception(msg) + + high_pri_freq = max(self.base_freqs_info.keys()) + high_pri_cores = base_freqs_info[high_pri_freq] + _base_freqs_info = {} + for index, _ in enumerate(cpu_topos): + _base_freqs_info[index] = 1 if index in high_pri_cores else 0 + + return _base_freqs_info + + def init_vms_params(self): + self.vm = self.vcpu_map = self.vcpu_lst = self.vm_dut = \ + self.guest_session = self.is_guest_on = self.is_vm_on = None + # vm config + self.vm_name = 'vm0' + self.vm_max_ch = 8 + self.vm_log_dir = '/tmp/powermonitor' + self.create_powermonitor_folder() + + def create_powermonitor_folder(self): + # create temporary folder for power monitor + cmd = 'mkdir -p {0}; chmod 777 {0}'.format(self.vm_log_dir) + self.d_a_con(cmd) + + def start_vm(self): + # set vm initialize parameters + self.init_vms_params() + # start vm + self.vm = LibvirtKvm(self.dut, self.vm_name, self.suite_name) + # pass pf to virtual machine + pci_addr = self.dut.get_port_pci(self.dut_ports[0]) + # add channel + ch_name = 'virtio.serial.port.poweragent.{0}' + vm_path = os.path.join(self.vm_log_dir, '{0}.{1}') + for cnt in range(self.vm_max_ch): + channel = { + 'path': vm_path.format(self.vm_name, cnt), + 'name': ch_name.format(cnt)} + self.vm.add_vm_virtio_serial_channel(**channel) + # boot up vm + self.vm_dut = self.vm.start() + self.is_vm_on = True + self.verify(self.vm_dut, "create vm_dut fail !") + self.add_console(self.vm_dut.session) + # get virtual machine cpu cores + _vcpu_map = self.vm.get_vm_cpu() + self.vcpu_map = [int(item) for item in _vcpu_map] + self.vcpu_lst = [int(item['core']) for item in self.vm_dut.cores] + + def close_vm(self): + # close vm + if self.is_vm_on: + if self.guest_session: + self.vm_dut.close_session(self.guest_session) + self.guest_session = None + self.vm.stop() + self.is_vm_on = False + self.vm = None + self.dut.virt_exit() + cmd_fmt = 'virsh {0} {1} > /dev/null 2>&1'.format + cmds = [ + [cmd_fmt('shutdown', self.vm_name), '# '], + [cmd_fmt('undefine', self.vm_name), '# '], ] + self.d_a_con(cmds) + + def init_vm_power_mgr(self): + self.vm_power_mgr = self.prepare_binary('vm_power_manager') + + def start_vm_power_mgr(self): + eal_option = ( + ' ' + '-c {core_mask} ' + '-n {mem_channel} ' + '--no-pci ').format(**{ + 'core_mask': self.get_cores_mask("1S/3C/1T"), + 'mem_channel': self.dut.get_memory_channels(), }) + prompt = 'vmpower>' + option = eal_option + cmd = [' '.join([self.vm_power_mgr, option]), prompt, 30] + self.d_con(cmd) + self.is_mgr_on = True + + def set_vm_power_mgr(self): + vm_name = self.vm_name + cmds = [ + "add_vm %s" % vm_name, + "add_channels %s all" % vm_name, + 'set_channel_status %s all enabled' % vm_name, + "show_vm %s" % vm_name] + prompt = 'vmpower>' + self.d_con([[cmd, prompt] for cmd in cmds]) + + def close_vm_power_mgr(self): + if not self.is_mgr_on: + return + self.d_con(['quit', '# ', 15]) + self.is_mgr_on = False + + def host_set_query_enable(self): + return self.d_con( + ['set_query {} enable'.format(self.vm_name), 'vmpower>', 15]) + + def host_set_query_disable(self): + return self.d_con( + ['set_query {} disable'.format(self.vm_name), 'vmpower>', 15]) + + def init_guest_mgr(self): + name = 'vm_power_manager/guest_cli' + self.guest_cli = self.prepare_binary(name, host_crb=self.vm_dut) + self.guest_con_name = \ + '_'.join([self.vm_dut.NAME, name.replace('/', '-')]) + self.guest_session = self.vm_dut.create_session(self.guest_con_name) + self.add_console(self.guest_session) + + def start_guest_mgr(self): + prompt = r"vmpower\(guest\)>" + option = ( + ' ' + '-c {core_mask} ' + '-n {memory_channel} ' + '-m {memory_size} ' + '--no-pci ' + '--file-prefix={file_prefix} ' + '-- ' + '--vm-name={vm_name} ' + '--vcpu-list={vpus} ').format(**{ + 'core_mask': '0xff', + 'memory_channel': self.vm_dut.get_memory_channels(), + 'memory_size': 1024, + 'file_prefix': 'vmpower1', + 'vm_name': self.vm_name, + 'vpus': ','.join([str(core) for core in self.vcpu_lst]), + }) + guest_cmd = self.guest_cli + option + self.vm_g_con([guest_cmd, prompt, 120]) + self.is_guest_on = True + + def close_guest_mgr(self): + if not self.is_guest_on: + return + self.vm_g_con("quit") + self.is_guest_on = False + + def guest_set_cpu_freq_down(self, core_index): + return self.vm_g_con(['set_cpu_freq {} down'.format(core_index), + r"vmpower\(guest\)>", 20]) + + def guest_query_cpu_caps(self, core='all'): + return self.vm_g_con( + ['query_cpu_caps {}'.format(core), r"vmpower\(guest\)>", 20]) + + def guest_query_cpu_freq(self, core='all'): + return self.vm_g_con( + ['query_cpu_freq {}'.format(core), r"vmpower\(guest\)>", 20]) + + def run_test_pre(self): + # boot up binary processes + self.start_vm_power_mgr() + # set binary process command + self.set_vm_power_mgr() + # boot up binary processes + self.start_guest_mgr() + + def run_test_post(self): + # close all binary processes + self.close_guest_mgr() + self.close_vm_power_mgr() + + def check_cpupower_tool(self): + cmd = "whereis cpupower > /dev/null 2>&1; echo $?" + output = self.d_a_con(cmd) + status = True if output and output.strip() == "0" else False + msg = 'cpupower tool have not installed on DUT' + self.verify(status, msg) + + def check_policy_command_acked_output(self): + output = self.guest_set_cpu_freq_down(self.vcpu_lst[-1]) + expected = 'ACK received for message sent to host' + msg = "expected message '{}' not in output".format(expected) + status = expected in output + [self.logger.info(output) if not status else None] + self.verify(status, msg) + output = self.guest_set_cpu_freq_down(self.vcpu_lst[-1] + 1) + expected = 'Error sending message: Unknown error -1' + msg = "expected message '{}' not in output".format(expected) + status = expected in output + [self.logger.info(output) if not status else None] + self.verify(status, msg) + + def check_query_cpu_freqs_command(self): + # Query the CPU frequency for all CPU cores from VM side + self.host_set_query_enable() + + def get_cpu_attribute(cores): + freq_path_fmt = ("cat /sys/devices/system/cpu/cpu{0}" + "/cpufreq/cpuinfo_cur_freq").format + cmd = ";".join([freq_path_fmt(core) for core in cores]) + output = self.d_a_con(cmd) + freqs = [int(item) for item in output.splitlines()] + return freqs + + def check(core, freq, output): + expected = "Frequency of [{0}] vcore is {1}.".format(core, freq) + msg = "expected message '{}' not in output".format(expected) + self.verify(expected in output, msg) + # check one core + check_core = self.vcpu_lst[-1] + freqs = get_cpu_attribute([self.vcpu_map[-1]]) + output = self.guest_query_cpu_freq(check_core) + check(check_core, freqs[0], output) + # check all cores + freqs = get_cpu_attribute(self.vcpu_map) + output = self.guest_query_cpu_freq() + [check(check_core, freqs[index], output) + for index, check_core in enumerate(self.vcpu_lst)] + # disable query permission from VM, check the host CPU frequency + # won't be returned + self.host_set_query_disable() + output = self.guest_query_cpu_freq() + expected = "Error during frequency list reception." + msg = "expected message '{}' not in output".format(expected) + self.verify(expected in output, msg) + + def check_cpu_capability_on_vm(self): + self.host_set_query_enable() + # check the high priority core is recognized correctly. + turbo_status = 1 + + def check(index, output): + vcore = self.vcpu_lst[index] + pcore = self.vcpu_map[index] + pri = self.base_freqs_info[pcore] + expected = ( + "Capabilities of [{0}] vcore are: " + "turbo possibility: {1}, " + "is priority core: {2}.").format(vcore, turbo_status, pri) + msg = "expected message '{}' not in output".format(expected) + self.verify(expected in output, msg) + + output = self.guest_query_cpu_caps() + [check(index, output) + for index, _ in enumerate(self.vcpu_lst)] + # check no CPU info will be return. + output = self.guest_query_cpu_caps(self.vcpu_lst[-1] + 1) + expected = 'Invalid parameter provided' + msg = "expected message '{}' not in output".format(expected) + self.verify(expected in output, msg) + # check the host CPU capability won't be returned. + self.host_set_query_disable() + output = self.guest_query_cpu_caps() + expected = "Error during capabilities reception" + msg = "expected message '{}' not in output".format(expected) + self.verify(expected in output, msg) + + def verify_policy_command_acked_action(self): + except_content = None + msg = "begin test policy command acked action ..." + self.logger.info(msg) + try: + self.run_test_pre() + self.check_policy_command_acked_output() + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content = e + finally: + self.run_test_post() + + # check verify result + if except_content: + raise VerifyFailure(except_content) + msg = "test policy command acked action successful !!!" + self.logger.info(msg) + + def verify_query_cpu_freqs_from_vm(self): + except_content = None + msg = "begin test query cpu freqs from vm ..." + self.logger.info(msg) + try: + self.run_test_pre() + self.check_query_cpu_freqs_command() + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content = e + finally: + self.run_test_post() + + # check verify result + if except_content: + raise VerifyFailure(except_content) + msg = "test query cpu freqs from vm successful !!!" + self.logger.info(msg) + + def verify_query_cpu_capability(self): + except_content = None + msg = "begin test query cpu capability ..." + self.logger.info(msg) + try: + self.run_test_pre() + self.check_cpu_capability_on_vm() + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content = e + finally: + self.run_test_post() + + # check verify result + if except_content: + raise VerifyFailure(except_content) + msg = "test query cpu capability successful !!!" + self.logger.info(msg) + + def verify_power_driver(self): + expected_drv = 'acpi-cpufreq' + power_drv = self.get_sys_power_driver() + msg = "{0} should work with {1} driver on DUT".format( + self.suite_name, expected_drv) + self.verify(power_drv == expected_drv, msg) + + def preset_test_environment(self): + self.is_mgr_on = None + self.ext_con = {} + # modprobe msr module to let the application can get the CPU HW info + self.d_a_con('modprobe msr') + self.d_a_con('cpupower frequency-set -g userspace') + self.dut.init_core_list_uncached_linux() + # check if cpu support bpf feature + self.base_freqs_info = self.get_all_cpu_attrs() + # boot up vm + self.start_vm() + # init binary + self.init_vm_power_mgr() + self.init_guest_mgr() + + # + # Test cases. + # + + def set_up_all(self): + """ + Run at the start of each test suite. + """ + self.dut_ports = self.dut.get_ports(self.nic) + self.verify(len(self.dut_ports) >= 1, "Not enough ports") + self.check_cpupower_tool() + self.verify_power_driver() + # prepare testing environment + self.preset_test_environment() + + def tear_down_all(self): + """ + Run after each test suite. + """ + self.close_vm() + + def set_up(self): + """ + Run before each test case. + """ + pass + + def tear_down(self): + """ + Run after each test case. + """ + self.vm_dut.kill_all() + self.dut.kill_all() + + def test_policy_command_acked_action(self): + """ + Check VM can send power policy command to host and get acked + """ + self.verify_policy_command_acked_action() + + def test_query_cpu_freqs_from_vm(self): + """ + Query Host CPU frequency list from VM + """ + self.verify_query_cpu_freqs_from_vm() + + def test_query_cpu_capability(self): + """ + Query CPU capability from VM + """ + self.verify_query_cpu_capability() -- 2.21.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [dts] [PATCH V2 2/4] tests/power_bidirection_channel: upload automation script 2020-01-16 1:56 ` [dts] [PATCH V2 2/4] tests/power_bidirection_channel: upload automation script yufengmx @ 2020-01-16 2:27 ` Yao, Lei A 0 siblings, 0 replies; 10+ messages in thread From: Yao, Lei A @ 2020-01-16 2:27 UTC (permalink / raw) To: Mo, YufengX, dts > -----Original Message----- > From: Mo, YufengX <yufengx.mo@intel.com> > Sent: Thursday, January 16, 2020 9:56 AM > To: dts@dpdk.org; Yao, Lei A <lei.a.yao@intel.com> > Cc: Mo, YufengX <yufengx.mo@intel.com> > Subject: [dts][PATCH V2 2/4] tests/power_bidirection_channel: upload > automation script > > > upload automation script. > > Signed-off-by: yufengmx <yufengx.mo@intel.com> Acked-by: Lei Yao <lei.a.yao@intel.com> > --- > tests/TestSuite_power_bidirection_channel.py | 590 > +++++++++++++++++++ > 1 file changed, 590 insertions(+) > create mode 100644 tests/TestSuite_power_bidirection_channel.py > > diff --git a/tests/TestSuite_power_bidirection_channel.py > b/tests/TestSuite_power_bidirection_channel.py > new file mode 100644 > index 0000000..ed82fda > --- /dev/null > +++ b/tests/TestSuite_power_bidirection_channel.py > @@ -0,0 +1,590 @@ > +# BSD LICENSE > +# > +# Copyright(c) 2010-2020 Intel Corporation. All rights reserved. > +# All rights reserved. > +# > +# Redistribution and use in source and binary forms, with or without # > +modification, are permitted provided that the following conditions # > +are met: > +# > +# * Redistributions of source code must retain the above copyright > +# notice, this list of conditions and the following disclaimer. > +# * Redistributions in binary form must reproduce the above copyright > +# notice, this list of conditions and the following disclaimer in > +# the documentation and/or other materials provided with the > +# distribution. > +# * Neither the name of Intel Corporation nor the names of its > +# contributors may be used to endorse or promote products derived > +# from this software without specific prior written permission. > +# > +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND > CONTRIBUTORS # > +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > # > +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS > FOR # > +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE > COPYRIGHT # > +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, > INCIDENTAL, # > +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT > NOT # > +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF > USE, # > +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND > ON ANY # > +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # > +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF > THE USE # > +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH > DAMAGE. > + > +""" > +DPDK Test suite. > +virtual power manager test suite. > +""" > +import os > +import time > +import traceback > + > +from utils import create_mask as dts_create_mask from qemu_libvirt > +import LibvirtKvm from exception import VerifyFailure from test_case > +import TestCase > + > + > +class TestPowerBidirectionChannel(TestCase): > + # temporary file directory > + output_path = '/tmp' > + > + @property > + def target_dir(self): > + # get absolute directory of target source code > + target_dir = '/root' + self.dut.base_dir[1:] \ > + if self.dut.base_dir.startswith('~') else \ > + self.dut.base_dir > + return target_dir > + > + def get_cores_mask(self, config='all'): > + ports_socket = self.dut.get_numa_id(self.dut.get_ports()[0]) > + mask = dts_create_mask( > + self.dut.get_core_list(config, socket=ports_socket)) > + return mask > + > + def prepare_binary(self, name, host_crb=None): > + _host_crb = host_crb if host_crb else self.dut > + example_dir = "examples/" + name > + out = _host_crb.build_dpdk_apps('./' + example_dir) > + self.verify("Error" not in out, "Compilation error") > + self.verify("No such" not in out, "Compilation error") > + binary_dir = os.path.join(self.target_dir, example_dir, 'build') > + cmd = ["ls -F {0} | grep '*'".format(binary_dir), '# ', 5] > + exec_file = self.execute_cmds(cmd, name=_host_crb.session.name) > + binary_file = os.path.join(binary_dir, exec_file[:-1]) > + return binary_file > + > + def add_console(self, session): > + self.ext_con[session.name] = [ > + session.send_expect, > + session.session.get_output_all] > + > + def get_console(self, name): > + default_con_table = { > + self.dut.session.name: [ > + self.dut.send_expect, > + self.dut.get_session_output], > + self.dut.alt_session.name: [ > + self.dut.alt_session.send_expect, > + self.dut.alt_session.session.get_output_all]} > + if name not in default_con_table: > + return self.ext_con.get(name) or [None, None] > + else: > + return default_con_table.get(name) > + > + def execute_cmds(self, cmds, name='dut'): > + console, msg_pipe = self.get_console(name) > + if len(cmds) == 0: > + return > + if isinstance(cmds, (str, unicode)): > + cmds = [cmds, '# ', 5] > + if not isinstance(cmds[0], list): > + cmds = [cmds] > + outputs = [] if len(cmds) > 1 else '' > + for item in cmds: > + expected_items = item[1] > + if expected_items and isinstance(expected_items, (list, tuple)): > + check_output = True > + expected_str = expected_items[0] or '# ' > + else: > + check_output = False > + expected_str = expected_items or '# ' > + > + try: > + if len(item) == 3: > + timeout = int(item[2]) > + output = console(item[0], expected_str, timeout) > + output = msg_pipe() if not output else output > + else: > + output = console(item[0], expected_str) > + output = msg_pipe() if not output else output > + except Exception as e: > + msg = "execute '{0}' timeout".format(item[0]) > + raise Exception(msg) > + time.sleep(1) > + if len(cmds) > 1: > + outputs.append(output) > + else: > + outputs = output > + > + return outputs > + > + def d_con(self, cmds): > + return self.execute_cmds(cmds, name=self.dut.session.name) > + > + def d_a_con(self, cmds): > + return self.execute_cmds(cmds, name=self.dut.alt_session.name) > + > + def vm_con(self, cmds): > + return self.execute_cmds(cmds, name=self.vm_dut.session.name) > + > + def vm_g_con(self, cmds): > + return self.execute_cmds(cmds, name=self.guest_con_name) > + > + def get_sys_power_driver(self): > + drv_file = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver" > + output = self.d_a_con('cat ' + drv_file) > + if not output: > + msg = 'unknown power driver' > + self.verify(False, msg) > + drv_name = output.splitlines()[0].strip() > + return drv_name > + > + @property > + def is_support_pbf(self): > + # check if cpu support bpf feature > + cpu_attr = r'/sys/devices/system/cpu/cpu0/cpufreq/base_frequency' > + cmd = "ls {0}".format(cpu_attr) > + self.d_a_con(cmd) > + cmd = "echo $?" > + output = self.d_a_con(cmd) > + ret = True if output == "0" else False > + return ret > + > + def get_all_cpu_attrs(self): > + ''' > + get all cpus' base_frequency value, if not support pbf, set all to 0 > + ''' > + if not self.is_support_pbf: > + cpu_topos = self.dut.get_all_cores() > + _base_freqs_info = {} > + for index, _ in enumerate(cpu_topos): > + _base_freqs_info[index] = 0 > + return _base_freqs_info > + # if cpu support high priority core > + key_values = ['base_frequency', > + 'cpuinfo_max_freq', > + 'cpuinfo_min_freq'] > + freq = r'/sys/devices/system/cpu/cpu{0}/cpufreq/{1}'.format > + # use dut alt session to get dut platform cpu base frequency attribute > + cpu_topos = self.dut.get_all_cores() > + cpu_info = {} > + for cpu_topo in cpu_topos: > + cpu_id = int(cpu_topo['thread']) > + cpu_info[cpu_id] = {} > + cpu_info[cpu_id]['socket'] = cpu_topo['socket'] > + cpu_info[cpu_id]['core'] = cpu_topo['core'] > + > + for key_value in key_values: > + cmds = [] > + for cpu_id in sorted(cpu_info.keys()): > + cmds.append('cat {0}'.format(freq(cpu_id, key_value))) > + output = self.d_a_con(';'.join(cmds)) > + freqs = [int(item) for item in output.splitlines()] > + for index, cpu_id in enumerate(sorted(cpu_info.keys())): > + cpu_info[cpu_id][key_value] = freqs[index] > + > + # get high priority core and normal core > + base_freqs_info = {} > + for core_index, value in cpu_info.iteritems(): > + base_frequency = value.get('base_frequency') > + base_freqs_info.setdefault(base_frequency, []).append(core_index) > + base_freqs = base_freqs_info.keys() > + # cpu should have high priority core and normal core > + # high priority core frequency is higher than normal core frequency > + if len(base_freqs) <= 1 or \ > + not all([len(value) for value in base_freqs_info.values()]): > + msg = 'current cpu has no high priority core' > + raise Exception(msg) > + > + high_pri_freq = max(self.base_freqs_info.keys()) > + high_pri_cores = base_freqs_info[high_pri_freq] > + _base_freqs_info = {} > + for index, _ in enumerate(cpu_topos): > + _base_freqs_info[index] = 1 if index in high_pri_cores else > + 0 > + > + return _base_freqs_info > + > + def init_vms_params(self): > + self.vm = self.vcpu_map = self.vcpu_lst = self.vm_dut = \ > + self.guest_session = self.is_guest_on = self.is_vm_on = None > + # vm config > + self.vm_name = 'vm0' > + self.vm_max_ch = 8 > + self.vm_log_dir = '/tmp/powermonitor' > + self.create_powermonitor_folder() > + > + def create_powermonitor_folder(self): > + # create temporary folder for power monitor > + cmd = 'mkdir -p {0}; chmod 777 {0}'.format(self.vm_log_dir) > + self.d_a_con(cmd) > + > + def start_vm(self): > + # set vm initialize parameters > + self.init_vms_params() > + # start vm > + self.vm = LibvirtKvm(self.dut, self.vm_name, self.suite_name) > + # pass pf to virtual machine > + pci_addr = self.dut.get_port_pci(self.dut_ports[0]) > + # add channel > + ch_name = 'virtio.serial.port.poweragent.{0}' > + vm_path = os.path.join(self.vm_log_dir, '{0}.{1}') > + for cnt in range(self.vm_max_ch): > + channel = { > + 'path': vm_path.format(self.vm_name, cnt), > + 'name': ch_name.format(cnt)} > + self.vm.add_vm_virtio_serial_channel(**channel) > + # boot up vm > + self.vm_dut = self.vm.start() > + self.is_vm_on = True > + self.verify(self.vm_dut, "create vm_dut fail !") > + self.add_console(self.vm_dut.session) > + # get virtual machine cpu cores > + _vcpu_map = self.vm.get_vm_cpu() > + self.vcpu_map = [int(item) for item in _vcpu_map] > + self.vcpu_lst = [int(item['core']) for item in > + self.vm_dut.cores] > + > + def close_vm(self): > + # close vm > + if self.is_vm_on: > + if self.guest_session: > + self.vm_dut.close_session(self.guest_session) > + self.guest_session = None > + self.vm.stop() > + self.is_vm_on = False > + self.vm = None > + self.dut.virt_exit() > + cmd_fmt = 'virsh {0} {1} > /dev/null 2>&1'.format > + cmds = [ > + [cmd_fmt('shutdown', self.vm_name), '# '], > + [cmd_fmt('undefine', self.vm_name), '# '], ] > + self.d_a_con(cmds) > + > + def init_vm_power_mgr(self): > + self.vm_power_mgr = self.prepare_binary('vm_power_manager') > + > + def start_vm_power_mgr(self): > + eal_option = ( > + ' ' > + '-c {core_mask} ' > + '-n {mem_channel} ' > + '--no-pci ').format(**{ > + 'core_mask': self.get_cores_mask("1S/3C/1T"), > + 'mem_channel': self.dut.get_memory_channels(), }) > + prompt = 'vmpower>' > + option = eal_option > + cmd = [' '.join([self.vm_power_mgr, option]), prompt, 30] > + self.d_con(cmd) > + self.is_mgr_on = True > + > + def set_vm_power_mgr(self): > + vm_name = self.vm_name > + cmds = [ > + "add_vm %s" % vm_name, > + "add_channels %s all" % vm_name, > + 'set_channel_status %s all enabled' % vm_name, > + "show_vm %s" % vm_name] > + prompt = 'vmpower>' > + self.d_con([[cmd, prompt] for cmd in cmds]) > + > + def close_vm_power_mgr(self): > + if not self.is_mgr_on: > + return > + self.d_con(['quit', '# ', 15]) > + self.is_mgr_on = False > + > + def host_set_query_enable(self): > + return self.d_con( > + ['set_query {} enable'.format(self.vm_name), 'vmpower>', > + 15]) > + > + def host_set_query_disable(self): > + return self.d_con( > + ['set_query {} disable'.format(self.vm_name), 'vmpower>', > + 15]) > + > + def init_guest_mgr(self): > + name = 'vm_power_manager/guest_cli' > + self.guest_cli = self.prepare_binary(name, host_crb=self.vm_dut) > + self.guest_con_name = \ > + '_'.join([self.vm_dut.NAME, name.replace('/', '-')]) > + self.guest_session = self.vm_dut.create_session(self.guest_con_name) > + self.add_console(self.guest_session) > + > + def start_guest_mgr(self): > + prompt = r"vmpower\(guest\)>" > + option = ( > + ' ' > + '-c {core_mask} ' > + '-n {memory_channel} ' > + '-m {memory_size} ' > + '--no-pci ' > + '--file-prefix={file_prefix} ' > + '-- ' > + '--vm-name={vm_name} ' > + '--vcpu-list={vpus} ').format(**{ > + 'core_mask': '0xff', > + 'memory_channel': self.vm_dut.get_memory_channels(), > + 'memory_size': 1024, > + 'file_prefix': 'vmpower1', > + 'vm_name': self.vm_name, > + 'vpus': ','.join([str(core) for core in self.vcpu_lst]), > + }) > + guest_cmd = self.guest_cli + option > + self.vm_g_con([guest_cmd, prompt, 120]) > + self.is_guest_on = True > + > + def close_guest_mgr(self): > + if not self.is_guest_on: > + return > + self.vm_g_con("quit") > + self.is_guest_on = False > + > + def guest_set_cpu_freq_down(self, core_index): > + return self.vm_g_con(['set_cpu_freq {} down'.format(core_index), > + r"vmpower\(guest\)>", 20]) > + > + def guest_query_cpu_caps(self, core='all'): > + return self.vm_g_con( > + ['query_cpu_caps {}'.format(core), r"vmpower\(guest\)>", > + 20]) > + > + def guest_query_cpu_freq(self, core='all'): > + return self.vm_g_con( > + ['query_cpu_freq {}'.format(core), r"vmpower\(guest\)>", > + 20]) > + > + def run_test_pre(self): > + # boot up binary processes > + self.start_vm_power_mgr() > + # set binary process command > + self.set_vm_power_mgr() > + # boot up binary processes > + self.start_guest_mgr() > + > + def run_test_post(self): > + # close all binary processes > + self.close_guest_mgr() > + self.close_vm_power_mgr() > + > + def check_cpupower_tool(self): > + cmd = "whereis cpupower > /dev/null 2>&1; echo $?" > + output = self.d_a_con(cmd) > + status = True if output and output.strip() == "0" else False > + msg = 'cpupower tool have not installed on DUT' > + self.verify(status, msg) > + > + def check_policy_command_acked_output(self): > + output = self.guest_set_cpu_freq_down(self.vcpu_lst[-1]) > + expected = 'ACK received for message sent to host' > + msg = "expected message '{}' not in output".format(expected) > + status = expected in output > + [self.logger.info(output) if not status else None] > + self.verify(status, msg) > + output = self.guest_set_cpu_freq_down(self.vcpu_lst[-1] + 1) > + expected = 'Error sending message: Unknown error -1' > + msg = "expected message '{}' not in output".format(expected) > + status = expected in output > + [self.logger.info(output) if not status else None] > + self.verify(status, msg) > + > + def check_query_cpu_freqs_command(self): > + # Query the CPU frequency for all CPU cores from VM side > + self.host_set_query_enable() > + > + def get_cpu_attribute(cores): > + freq_path_fmt = ("cat /sys/devices/system/cpu/cpu{0}" > + "/cpufreq/cpuinfo_cur_freq").format > + cmd = ";".join([freq_path_fmt(core) for core in cores]) > + output = self.d_a_con(cmd) > + freqs = [int(item) for item in output.splitlines()] > + return freqs > + > + def check(core, freq, output): > + expected = "Frequency of [{0}] vcore is {1}.".format(core, freq) > + msg = "expected message '{}' not in output".format(expected) > + self.verify(expected in output, msg) > + # check one core > + check_core = self.vcpu_lst[-1] > + freqs = get_cpu_attribute([self.vcpu_map[-1]]) > + output = self.guest_query_cpu_freq(check_core) > + check(check_core, freqs[0], output) > + # check all cores > + freqs = get_cpu_attribute(self.vcpu_map) > + output = self.guest_query_cpu_freq() > + [check(check_core, freqs[index], output) > + for index, check_core in enumerate(self.vcpu_lst)] > + # disable query permission from VM, check the host CPU frequency > + # won't be returned > + self.host_set_query_disable() > + output = self.guest_query_cpu_freq() > + expected = "Error during frequency list reception." > + msg = "expected message '{}' not in output".format(expected) > + self.verify(expected in output, msg) > + > + def check_cpu_capability_on_vm(self): > + self.host_set_query_enable() > + # check the high priority core is recognized correctly. > + turbo_status = 1 > + > + def check(index, output): > + vcore = self.vcpu_lst[index] > + pcore = self.vcpu_map[index] > + pri = self.base_freqs_info[pcore] > + expected = ( > + "Capabilities of [{0}] vcore are: " > + "turbo possibility: {1}, " > + "is priority core: {2}.").format(vcore, turbo_status, pri) > + msg = "expected message '{}' not in output".format(expected) > + self.verify(expected in output, msg) > + > + output = self.guest_query_cpu_caps() > + [check(index, output) > + for index, _ in enumerate(self.vcpu_lst)] > + # check no CPU info will be return. > + output = self.guest_query_cpu_caps(self.vcpu_lst[-1] + 1) > + expected = 'Invalid parameter provided' > + msg = "expected message '{}' not in output".format(expected) > + self.verify(expected in output, msg) > + # check the host CPU capability won't be returned. > + self.host_set_query_disable() > + output = self.guest_query_cpu_caps() > + expected = "Error during capabilities reception" > + msg = "expected message '{}' not in output".format(expected) > + self.verify(expected in output, msg) > + > + def verify_policy_command_acked_action(self): > + except_content = None > + msg = "begin test policy command acked action ..." > + self.logger.info(msg) > + try: > + self.run_test_pre() > + self.check_policy_command_acked_output() > + except Exception as e: > + self.logger.error(traceback.format_exc()) > + except_content = e > + finally: > + self.run_test_post() > + > + # check verify result > + if except_content: > + raise VerifyFailure(except_content) > + msg = "test policy command acked action successful !!!" > + self.logger.info(msg) > + > + def verify_query_cpu_freqs_from_vm(self): > + except_content = None > + msg = "begin test query cpu freqs from vm ..." > + self.logger.info(msg) > + try: > + self.run_test_pre() > + self.check_query_cpu_freqs_command() > + except Exception as e: > + self.logger.error(traceback.format_exc()) > + except_content = e > + finally: > + self.run_test_post() > + > + # check verify result > + if except_content: > + raise VerifyFailure(except_content) > + msg = "test query cpu freqs from vm successful !!!" > + self.logger.info(msg) > + > + def verify_query_cpu_capability(self): > + except_content = None > + msg = "begin test query cpu capability ..." > + self.logger.info(msg) > + try: > + self.run_test_pre() > + self.check_cpu_capability_on_vm() > + except Exception as e: > + self.logger.error(traceback.format_exc()) > + except_content = e > + finally: > + self.run_test_post() > + > + # check verify result > + if except_content: > + raise VerifyFailure(except_content) > + msg = "test query cpu capability successful !!!" > + self.logger.info(msg) > + > + def verify_power_driver(self): > + expected_drv = 'acpi-cpufreq' > + power_drv = self.get_sys_power_driver() > + msg = "{0} should work with {1} driver on DUT".format( > + self.suite_name, expected_drv) > + self.verify(power_drv == expected_drv, msg) > + > + def preset_test_environment(self): > + self.is_mgr_on = None > + self.ext_con = {} > + # modprobe msr module to let the application can get the CPU HW info > + self.d_a_con('modprobe msr') > + self.d_a_con('cpupower frequency-set -g userspace') > + self.dut.init_core_list_uncached_linux() > + # check if cpu support bpf feature > + self.base_freqs_info = self.get_all_cpu_attrs() > + # boot up vm > + self.start_vm() > + # init binary > + self.init_vm_power_mgr() > + self.init_guest_mgr() > + > + # > + # Test cases. > + # > + > + def set_up_all(self): > + """ > + Run at the start of each test suite. > + """ > + self.dut_ports = self.dut.get_ports(self.nic) > + self.verify(len(self.dut_ports) >= 1, "Not enough ports") > + self.check_cpupower_tool() > + self.verify_power_driver() > + # prepare testing environment > + self.preset_test_environment() > + > + def tear_down_all(self): > + """ > + Run after each test suite. > + """ > + self.close_vm() > + > + def set_up(self): > + """ > + Run before each test case. > + """ > + pass > + > + def tear_down(self): > + """ > + Run after each test case. > + """ > + self.vm_dut.kill_all() > + self.dut.kill_all() > + > + def test_policy_command_acked_action(self): > + """ > + Check VM can send power policy command to host and get acked > + """ > + self.verify_policy_command_acked_action() > + > + def test_query_cpu_freqs_from_vm(self): > + """ > + Query Host CPU frequency list from VM > + """ > + self.verify_query_cpu_freqs_from_vm() > + > + def test_query_cpu_capability(self): > + """ > + Query CPU capability from VM > + """ > + self.verify_query_cpu_capability() > -- > 2.21.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [dts] [PATCH V2 3/4] test_plans/power_bidirection_channel: add test plan index 2020-01-16 1:56 [dts] [PATCH V2 0/4] power_bidirection_channel: upload automation script and test plan yufengmx 2020-01-16 1:56 ` [dts] [PATCH V2 1/4] tests/power_bidirection_channel: vm config file yufengmx 2020-01-16 1:56 ` [dts] [PATCH V2 2/4] tests/power_bidirection_channel: upload automation script yufengmx @ 2020-01-16 1:56 ` yufengmx 2020-01-16 2:28 ` Yao, Lei A 2020-01-16 1:56 ` [dts] [PATCH V2 4/4] test_plans/power_bidirection_channel: upload test plan yufengmx 2020-01-16 6:18 ` [dts] [PATCH V2 0/4] power_bidirection_channel: upload automation script and " Tu, Lijuan 4 siblings, 1 reply; 10+ messages in thread From: yufengmx @ 2020-01-16 1:56 UTC (permalink / raw) To: dts, lei.a.yao; +Cc: yufengmx add test plan index. Signed-off-by: yufengmx <yufengx.mo@intel.com> --- test_plans/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/test_plans/index.rst b/test_plans/index.rst index c439870..de4ead7 100644 --- a/test_plans/index.rst +++ b/test_plans/index.rst @@ -164,6 +164,7 @@ The following are the test plans for the DPDK DTS automated test system. vhost_user_live_migration_test_plan vm_power_manager_test_plan vm_pw_mgmt_policy_test_plan + power_bidirection_channel_test_plan power_empty_poll_test_plan power_pbf_test_plan power_pstate_test_plan -- 2.21.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [dts] [PATCH V2 3/4] test_plans/power_bidirection_channel: add test plan index 2020-01-16 1:56 ` [dts] [PATCH V2 3/4] test_plans/power_bidirection_channel: add test plan index yufengmx @ 2020-01-16 2:28 ` Yao, Lei A 0 siblings, 0 replies; 10+ messages in thread From: Yao, Lei A @ 2020-01-16 2:28 UTC (permalink / raw) To: Mo, YufengX, dts > -----Original Message----- > From: Mo, YufengX <yufengx.mo@intel.com> > Sent: Thursday, January 16, 2020 9:56 AM > To: dts@dpdk.org; Yao, Lei A <lei.a.yao@intel.com> > Cc: Mo, YufengX <yufengx.mo@intel.com> > Subject: [dts][PATCH V2 3/4] test_plans/power_bidirection_channel: add > test plan index > > > add test plan index. > > Signed-off-by: yufengmx <yufengx.mo@intel.com> Acked-by: Lei Yao <lei.a.yao@intel.com> > --- > test_plans/index.rst | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/test_plans/index.rst b/test_plans/index.rst index > c439870..de4ead7 100644 > --- a/test_plans/index.rst > +++ b/test_plans/index.rst > @@ -164,6 +164,7 @@ The following are the test plans for the DPDK DTS > automated test system. > vhost_user_live_migration_test_plan > vm_power_manager_test_plan > vm_pw_mgmt_policy_test_plan > + power_bidirection_channel_test_plan > power_empty_poll_test_plan > power_pbf_test_plan > power_pstate_test_plan > -- > 2.21.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [dts] [PATCH V2 4/4] test_plans/power_bidirection_channel: upload test plan 2020-01-16 1:56 [dts] [PATCH V2 0/4] power_bidirection_channel: upload automation script and test plan yufengmx ` (2 preceding siblings ...) 2020-01-16 1:56 ` [dts] [PATCH V2 3/4] test_plans/power_bidirection_channel: add test plan index yufengmx @ 2020-01-16 1:56 ` yufengmx 2020-01-16 2:28 ` Yao, Lei A 2020-01-16 6:18 ` [dts] [PATCH V2 0/4] power_bidirection_channel: upload automation script and " Tu, Lijuan 4 siblings, 1 reply; 10+ messages in thread From: yufengmx @ 2020-01-16 1:56 UTC (permalink / raw) To: dts, lei.a.yao; +Cc: yufengmx upload test plan. Signed-off-by: yufengmx <yufengx.mo@intel.com> --- .../power_bidirection_channel_test_plan.rst | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 test_plans/power_bidirection_channel_test_plan.rst diff --git a/test_plans/power_bidirection_channel_test_plan.rst b/test_plans/power_bidirection_channel_test_plan.rst new file mode 100644 index 0000000..e26a603 --- /dev/null +++ b/test_plans/power_bidirection_channel_test_plan.rst @@ -0,0 +1,173 @@ +.. Copyright (c) <2010-2020>, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + - Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + OF THE POSSIBILITY OF SUCH DAMAGE. + +=================================== +power bidirection channel test plan +=================================== + +preparation work +================ +1. Turn on speedstep option in BIOS. +2. Turn on CPU C3 and C6. +3. Turn on turbo in BIOS. +4. Disable intel_pstate in Linux kernel command ``intel_pstate=disable``. +5. modprobe msr module to let the application can get the CPU HW info. +6. Let user space can control the CPU frequency:: + + cpupower frequency-set -g userspace + +7. set a folder:: + + mkdir /tmp/powermonitor + chmod 777 /tmp/powermonitor + + +Test Case 1 : Check VM can send power policy command to host and get acked +========================================================================== +Step 1. Launch VM using libvirt:: + + virsh start [VM name] + +Step 2. Launch VM power manager example on the host to monitor the channel from VM:: + + ./examples/vm_power_manager/build/vm_power_mgr -l 12-14 -n 4 --no-pci + vmpower> add_vm [vm name] + vmpower> add_channels [vm name] all + vmpower> set_channel_status [vm name] all enabled + vmpower> show_vm [vm name] + + If VM name is ubuntu, the command as following: + vmpower> add_vm ubuntu + vmpower> add_channels ubuntu all + vmpower> set_channel_status ubuntu all enabled + vmpower> show_vm ubuntu + +Step 3. In the VM, launch guest_vm_power_mgr to set and send the power manager policy to the host power example:: + + ./examples/vm_power_manager/guest_cli/build/guest_vm_power_mgr -c 0xff -n 4 -m 1024 --no-pci --file-prefix=vm_power -- --vm-name=ubuntu --vcpu-list=0-7 + + Send command to the core 7 on host APP: + vmpower(guest)> set_cpu_freq 7 down + + Check following info will be returned for the ACK activity, as following: + ACK received for message sent to host. + + If command can't be executed, NACK will be returned, as following: + NACK received for message sent to host. + +Step 4. Set frequency on core which is out of the VM's core scope:: + + For example, the vcpu range is 0-7, we set command to vcpu number 9 as following: + vmpower(guest)> set_cpu_freq 9 down + GUEST_CHANNEL: Channel is not connected + Error sending message: Unknown error -1 + + +Test Case 2 : Query Host CPU frequency list from VM +=================================================== +Step 1. Launch VM using libvirt:: + + virsh start [VM name] + +Step 2. Launch VM power manager example on the host to monitor the channel from VM:: + + ./examples/vm_power_manager/build/vm_power_mgr -l 12-14 -n 4 --no-pci + vmpower> add_vm [vm name] + vmpower> add_channels [vm name] all + vmpower> set_channel_status [vm name] all enabled + vmpower> show_vm [vm name] + vmpower> set_query <vm_name> <enable|disable> + +Step 3. Enable the query permission for target VM from host vm_power_mgr example:: + + Command format: set_query <vm_name> <enable|disable> + if vm name is ubuntu,command as following: + vmpower> set_query ubuntu enable + +Step 4. Query the CPU frequency for all CPU cores from VM side:: + + ./examples/vm_power_manager/guest_cli/build/guest_vm_power_mgr -c 0xff -n 4 -m 1024 --no-pci --file-prefix=vm_power -- --vm-name=ubuntu --vcpu-list=0-7 + vmpower> query_cpu_freq <core_num> | all + + Check vcpu 0~7 frequency info will be returned, for example: + Frequency of [0] vcore is 2300000. + Frequency of [1] vcore is 2200000. + Frequency of [2] vcore is 2800000. + Frequency of [3] vcore is 2300000. + Frequency of [4] vcore is 2300000. + Frequency of [5] vcore is 2300000. + Frequency of [6] vcore is 2300000. + Frequency of [7] vcore is 2300000. + +Step 5. Disable query permission from VM, check the host CPU frequency won't be returned:: + + at host side, disable query permission by vm_power_mgr example: + vmpower> set_query ubuntu disable + + at VM side, query CPU frequency again, this action should not be executed successfully, log as following: + vmpower(guest)> query_cpu_freq all + GUEST_CLI: Error receiving message. + Error during frequency list reception. + + +Test Case 3: Query CPU capability from VM +========================================= +Step1~3. The same as test case 2 + +Step4: Query all the valid CPU core capability of host, check all cores' information is returned. Check the high priority core is recognized correctly:: + + For example, core 2 is returned as high priority core: + vmpower(guest)> query_cpu_caps all + Capabilities of [0] vcore are: turbo possibility: 1, is priority core: 0. + Capabilities of [1] vcore are: turbo possibility: 1, is priority core: 0. + Capabilities of [2] vcore are: turbo possibility: 1, is priority core: 1. + Capabilities of [3] vcore are: turbo possibility: 1, is priority core: 0. + Capabilities of [4] vcore are: turbo possibility: 1, is priority core: 0. + Capabilities of [5] vcore are: turbo possibility: 1, is priority core: 0. + Capabilities of [6] vcore are: turbo possibility: 1, is priority core: 0. + Capabilities of [7] vcore are: turbo possibility: 1, is priority core: 0. + +Step 5: Query CPU capability for core out of scope, check no CPU info will be return:: + + For example, the valid vcpu range is 0~7, query cpu capability of core 9 should return error as following: + vmpower(guest)> query_cpu_caps 9 + Invalid parameter provided. + +Step 6: Disable query permission from VM, check the host CPU capability won't be returned:: + + at host side, disable query permission by vm_power_mgr example: + vmpower> set_query ubuntu disable + + at VM side, query CPU capability again, this action should not be executed successfully, log as following: + vmpower(guest)> query_cpu_caps all + GUEST_CLI: Error receiving message. + Error during capabilities reception. \ No newline at end of file -- 2.21.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [dts] [PATCH V2 4/4] test_plans/power_bidirection_channel: upload test plan 2020-01-16 1:56 ` [dts] [PATCH V2 4/4] test_plans/power_bidirection_channel: upload test plan yufengmx @ 2020-01-16 2:28 ` Yao, Lei A 0 siblings, 0 replies; 10+ messages in thread From: Yao, Lei A @ 2020-01-16 2:28 UTC (permalink / raw) To: Mo, YufengX, dts > -----Original Message----- > From: Mo, YufengX <yufengx.mo@intel.com> > Sent: Thursday, January 16, 2020 9:57 AM > To: dts@dpdk.org; Yao, Lei A <lei.a.yao@intel.com> > Cc: Mo, YufengX <yufengx.mo@intel.com> > Subject: [dts][PATCH V2 4/4] test_plans/power_bidirection_channel: upload > test plan > > > upload test plan. > > Signed-off-by: yufengmx <yufengx.mo@intel.com> Acked-by: Lei Yao <lei.a.yao@intel.com> > --- > .../power_bidirection_channel_test_plan.rst | 173 ++++++++++++++++++ > 1 file changed, 173 insertions(+) > create mode 100644 test_plans/power_bidirection_channel_test_plan.rst > > diff --git a/test_plans/power_bidirection_channel_test_plan.rst > b/test_plans/power_bidirection_channel_test_plan.rst > new file mode 100644 > index 0000000..e26a603 > --- /dev/null > +++ b/test_plans/power_bidirection_channel_test_plan.rst > @@ -0,0 +1,173 @@ > +.. Copyright (c) <2010-2020>, Intel Corporation > + All rights reserved. > + > + Redistribution and use in source and binary forms, with or without > + modification, are permitted provided that the following conditions > + are met: > + > + - Redistributions of source code must retain the above copyright > + notice, this list of conditions and the following disclaimer. > + > + - Redistributions in binary form must reproduce the above copyright > + notice, this list of conditions and the following disclaimer in > + the documentation and/or other materials provided with the > + distribution. > + > + - Neither the name of Intel Corporation nor the names of its > + contributors may be used to endorse or promote products derived > + from this software without specific prior written permission. > + > + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND > CONTRIBUTORS > + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT > NOT > + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND > FITNESS > + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE > + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, > INDIRECT, > + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES > + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE > GOODS OR > + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) > + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN > CONTRACT, > + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) > + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF > ADVISED > + OF THE POSSIBILITY OF SUCH DAMAGE. > + > +=================================== > +power bidirection channel test plan > +=================================== > + > +preparation work > +================ > +1. Turn on speedstep option in BIOS. > +2. Turn on CPU C3 and C6. > +3. Turn on turbo in BIOS. > +4. Disable intel_pstate in Linux kernel command ``intel_pstate=disable``. > +5. modprobe msr module to let the application can get the CPU HW info. > +6. Let user space can control the CPU frequency:: > + > + cpupower frequency-set -g userspace > + > +7. set a folder:: > + > + mkdir /tmp/powermonitor > + chmod 777 /tmp/powermonitor > + > + > +Test Case 1 : Check VM can send power policy command to host and get > +acked > +========================================================= > ============== > +=== > +Step 1. Launch VM using libvirt:: > + > + virsh start [VM name] > + > +Step 2. Launch VM power manager example on the host to monitor the > channel from VM:: > + > + ./examples/vm_power_manager/build/vm_power_mgr -l 12-14 -n 4 -- > no-pci > + vmpower> add_vm [vm name] > + vmpower> add_channels [vm name] all > + vmpower> set_channel_status [vm name] all enabled > + vmpower> show_vm [vm name] > + > + If VM name is ubuntu, the command as following: > + vmpower> add_vm ubuntu > + vmpower> add_channels ubuntu all > + vmpower> set_channel_status ubuntu all enabled > + vmpower> show_vm ubuntu > + > +Step 3. In the VM, launch guest_vm_power_mgr to set and send the power > manager policy to the host power example:: > + > + ./examples/vm_power_manager/guest_cli/build/guest_vm_power_mgr > -c > + 0xff -n 4 -m 1024 --no-pci --file-prefix=vm_power -- --vm-name=ubuntu > + --vcpu-list=0-7 > + > + Send command to the core 7 on host APP: > + vmpower(guest)> set_cpu_freq 7 down > + > + Check following info will be returned for the ACK activity, as following: > + ACK received for message sent to host. > + > + If command can't be executed, NACK will be returned, as following: > + NACK received for message sent to host. > + > +Step 4. Set frequency on core which is out of the VM's core scope:: > + > + For example, the vcpu range is 0-7, we set command to vcpu number 9 as > following: > + vmpower(guest)> set_cpu_freq 9 down > + GUEST_CHANNEL: Channel is not connected > + Error sending message: Unknown error -1 > + > + > +Test Case 2 : Query Host CPU frequency list from VM > +=================================================== > +Step 1. Launch VM using libvirt:: > + > + virsh start [VM name] > + > +Step 2. Launch VM power manager example on the host to monitor the > channel from VM:: > + > + ./examples/vm_power_manager/build/vm_power_mgr -l 12-14 -n 4 -- > no-pci > + vmpower> add_vm [vm name] > + vmpower> add_channels [vm name] all > + vmpower> set_channel_status [vm name] all enabled > + vmpower> show_vm [vm name] > + vmpower> set_query <vm_name> <enable|disable> > + > +Step 3. Enable the query permission for target VM from host > vm_power_mgr example:: > + > + Command format: set_query <vm_name> <enable|disable> > + if vm name is ubuntu,command as following: > + vmpower> set_query ubuntu enable > + > +Step 4. Query the CPU frequency for all CPU cores from VM side:: > + > + ./examples/vm_power_manager/guest_cli/build/guest_vm_power_mgr > -c 0xff -n 4 -m 1024 --no-pci --file-prefix=vm_power -- --vm-name=ubuntu -- > vcpu-list=0-7 > + vmpower> query_cpu_freq <core_num> | all > + > + Check vcpu 0~7 frequency info will be returned, for example: > + Frequency of [0] vcore is 2300000. > + Frequency of [1] vcore is 2200000. > + Frequency of [2] vcore is 2800000. > + Frequency of [3] vcore is 2300000. > + Frequency of [4] vcore is 2300000. > + Frequency of [5] vcore is 2300000. > + Frequency of [6] vcore is 2300000. > + Frequency of [7] vcore is 2300000. > + > +Step 5. Disable query permission from VM, check the host CPU frequency > won't be returned:: > + > + at host side, disable query permission by vm_power_mgr example: > + vmpower> set_query ubuntu disable > + > + at VM side, query CPU frequency again, this action should not be > executed successfully, log as following: > + vmpower(guest)> query_cpu_freq all > + GUEST_CLI: Error receiving message. > + Error during frequency list reception. > + > + > +Test Case 3: Query CPU capability from VM > +========================================= > +Step1~3. The same as test case 2 > + > +Step4: Query all the valid CPU core capability of host, check all cores' > information is returned. Check the high priority core is recognized correctly:: > + > + For example, core 2 is returned as high priority core: > + vmpower(guest)> query_cpu_caps all > + Capabilities of [0] vcore are: turbo possibility: 1, is priority core: 0. > + Capabilities of [1] vcore are: turbo possibility: 1, is priority core: 0. > + Capabilities of [2] vcore are: turbo possibility: 1, is priority core: 1. > + Capabilities of [3] vcore are: turbo possibility: 1, is priority core: 0. > + Capabilities of [4] vcore are: turbo possibility: 1, is priority core: 0. > + Capabilities of [5] vcore are: turbo possibility: 1, is priority core: 0. > + Capabilities of [6] vcore are: turbo possibility: 1, is priority core: 0. > + Capabilities of [7] vcore are: turbo possibility: 1, is priority core: 0. > + > +Step 5: Query CPU capability for core out of scope, check no CPU info will be > return:: > + > + For example, the valid vcpu range is 0~7, query cpu capability of core 9 > should return error as following: > + vmpower(guest)> query_cpu_caps 9 > + Invalid parameter provided. > + > +Step 6: Disable query permission from VM, check the host CPU capability > won't be returned:: > + > + at host side, disable query permission by vm_power_mgr example: > + vmpower> set_query ubuntu disable > + > + at VM side, query CPU capability again, this action should not be executed > successfully, log as following: > + vmpower(guest)> query_cpu_caps all > + GUEST_CLI: Error receiving message. > + Error during capabilities reception. > \ No newline at end of file > -- > 2.21.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [dts] [PATCH V2 0/4] power_bidirection_channel: upload automation script and test plan 2020-01-16 1:56 [dts] [PATCH V2 0/4] power_bidirection_channel: upload automation script and test plan yufengmx ` (3 preceding siblings ...) 2020-01-16 1:56 ` [dts] [PATCH V2 4/4] test_plans/power_bidirection_channel: upload test plan yufengmx @ 2020-01-16 6:18 ` Tu, Lijuan 4 siblings, 0 replies; 10+ messages in thread From: Tu, Lijuan @ 2020-01-16 6:18 UTC (permalink / raw) To: Mo, YufengX, dts, Yao, Lei A; +Cc: Mo, YufengX Applied the series. > -----Original Message----- > From: dts [mailto:dts-bounces@dpdk.org] On Behalf Of yufengmx > Sent: Thursday, January 16, 2020 9:56 AM > To: dts@dpdk.org; Yao, Lei A <lei.a.yao@intel.com> > Cc: Mo, YufengX <yufengx.mo@intel.com> > Subject: [dts] [PATCH V2 0/4] power_bidirection_channel: upload > automation script and test plan > > upload automation script and test plan > > v2: > - remove pf passthrough. > > yufengmx (4): > tests/power_bidirection_channel: vm config file > tests/power_bidirection_channel: upload automation script > test_plans/power_bidirection_channel: add test plan index > test_plans/power_bidirection_channel: upload test plan > > conf/power_bidirection_channel.cfg | 30 + > test_plans/index.rst | 1 + > .../power_bidirection_channel_test_plan.rst | 173 +++++ > tests/TestSuite_power_bidirection_channel.py | 590 ++++++++++++++++++ > 4 files changed, 794 insertions(+) > create mode 100644 conf/power_bidirection_channel.cfg > create mode 100644 test_plans/power_bidirection_channel_test_plan.rst > create mode 100644 tests/TestSuite_power_bidirection_channel.py > > -- > 2.21.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2020-01-16 6:18 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-01-16 1:56 [dts] [PATCH V2 0/4] power_bidirection_channel: upload automation script and test plan yufengmx 2020-01-16 1:56 ` [dts] [PATCH V2 1/4] tests/power_bidirection_channel: vm config file yufengmx 2020-01-16 2:27 ` Yao, Lei A 2020-01-16 1:56 ` [dts] [PATCH V2 2/4] tests/power_bidirection_channel: upload automation script yufengmx 2020-01-16 2:27 ` Yao, Lei A 2020-01-16 1:56 ` [dts] [PATCH V2 3/4] test_plans/power_bidirection_channel: add test plan index yufengmx 2020-01-16 2:28 ` Yao, Lei A 2020-01-16 1:56 ` [dts] [PATCH V2 4/4] test_plans/power_bidirection_channel: upload test plan yufengmx 2020-01-16 2:28 ` Yao, Lei A 2020-01-16 6:18 ` [dts] [PATCH V2 0/4] power_bidirection_channel: upload automation script and " Tu, Lijuan
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).