From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 30F53A3201 for ; Mon, 21 Oct 2019 09:44:34 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 1A9882C60; Mon, 21 Oct 2019 09:44:34 +0200 (CEST) Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by dpdk.org (Postfix) with ESMTP id 12F702C37 for ; Mon, 21 Oct 2019 09:44:31 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga004.fm.intel.com ([10.253.24.48]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 21 Oct 2019 00:44:31 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.67,322,1566889200"; d="scan'208";a="222382647" Received: from fmsmsx103.amr.corp.intel.com ([10.18.124.201]) by fmsmga004.fm.intel.com with ESMTP; 21 Oct 2019 00:44:29 -0700 Received: from fmsmsx115.amr.corp.intel.com (10.18.116.19) by FMSMSX103.amr.corp.intel.com (10.18.124.201) with Microsoft SMTP Server (TLS) id 14.3.439.0; Mon, 21 Oct 2019 00:44:30 -0700 Received: from shsmsx103.ccr.corp.intel.com (10.239.4.69) by fmsmsx115.amr.corp.intel.com (10.18.116.19) with Microsoft SMTP Server (TLS) id 14.3.439.0; Mon, 21 Oct 2019 00:44:30 -0700 Received: from shsmsx102.ccr.corp.intel.com ([169.254.2.176]) by SHSMSX103.ccr.corp.intel.com ([169.254.4.165]) with mapi id 14.03.0439.000; Mon, 21 Oct 2019 15:44:28 +0800 From: "Yao, Lei A" To: "Mo, YufengX" , "dts@dpdk.org" Thread-Topic: [dts][PATCH V1 1/3] tests/power_empty_poll: upload automation script Thread-Index: AQHVh9YiQ/pyoHQj/ESXOyVmKDhWH6dktsEw Date: Mon, 21 Oct 2019 07:44:27 +0000 Message-ID: <2DBBFF226F7CF64BAFCA79B681719D95497B706A@shsmsx102.ccr.corp.intel.com> References: <20191021061129.11958-1-yufengx.mo@intel.com> <20191021061129.11958-2-yufengx.mo@intel.com> In-Reply-To: <20191021061129.11958-2-yufengx.mo@intel.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-titus-metadata-40: eyJDYXRlZ29yeUxhYmVscyI6IiIsIk1ldGFkYXRhIjp7Im5zIjoiaHR0cDpcL1wvd3d3LnRpdHVzLmNvbVwvbnNcL0ludGVsMyIsImlkIjoiNmUyNjJkN2QtYzAzNi00NWRiLWIyNmUtYjdhOWIwNjJiNWY2IiwicHJvcHMiOlt7Im4iOiJDVFBDbGFzc2lmaWNhdGlvbiIsInZhbHMiOlt7InZhbHVlIjoiQ1RQX05UIn1dfV19LCJTdWJqZWN0TGFiZWxzIjpbXSwiVE1DVmVyc2lvbiI6IjE3LjEwLjE4MDQuNDkiLCJUcnVzdGVkTGFiZWxIYXNoIjoia0RZMDhFN2l6WnlOSlwvTGw3T29XZjRzQTFuWXNxbm9ja3Bidm1zTWNGSDNTYWVoNHpMUVlWUzVVZERSNWRpOXYifQ== x-ctpclassification: CTP_NT dlp-product: dlpe-windows dlp-version: 11.2.0.6 dlp-reaction: no-action x-originating-ip: [10.239.127.40] Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Subject: Re: [dts] [PATCH V1 1/3] tests/power_empty_poll: upload automation script X-BeenThere: dts@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: test suite reviews and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dts-bounces@dpdk.org Sender: "dts" > -----Original Message----- > From: Mo, YufengX > Sent: Monday, October 21, 2019 2:11 PM > To: dts@dpdk.org; Yao, Lei A > Cc: Mo, YufengX > Subject: [dts][PATCH V1 1/3] tests/power_empty_poll: upload automation > script >=20 >=20 > For packet processing workloads such as DPDK polling is continuous. > This means CPU cores always show 100% busy independent of how much > work those cores are doing. It is critical to accurately determine how bu= sy a > core is hugely important for the following reasons. >=20 > Signed-off-by: yufengmx Acked-by: lei yao > --- > tests/TestSuite_power_empty_poll.py | 359 > ++++++++++++++++++++++++++++ > 1 file changed, 359 insertions(+) > create mode 100644 tests/TestSuite_power_empty_poll.py >=20 > diff --git a/tests/TestSuite_power_empty_poll.py > b/tests/TestSuite_power_empty_poll.py > new file mode 100644 > index 0000000..3556e74 > --- /dev/null > +++ b/tests/TestSuite_power_empty_poll.py > @@ -0,0 +1,359 @@ > +# BSD LICENSE > +# > +# Copyright(c) 2010-2019 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. > +l3fwd-power power management test suite. > +""" > +import os > +import time > +import textwrap > +import traceback > +from copy import deepcopy > + > +from utils import create_mask as dts_create_mask from test_case import > +TestCase > + > +from packet import Packet > +from pktgen import TRANSMIT_CONT > + > + > +class TestPowerEmptPoll(TestCase): > + TRAIN =3D 'train' > + NOTRAIN =3D 'no-train' > + MED =3D 'med_threshold' > + HIGH =3D 'high_threshold' > + query_min_freq =3D '/tmp/cpu_min.log' > + query_max_freq =3D '/tmp/cpu_max.log' > + output_path =3D '/tmp' > + > + @property > + def target_dir(self): > + # get absolute directory of target source code > + target_dir =3D '/root' + self.dut.base_dir[1:] \ > + if self.dut.base_dir.startswith('~') else \ > + self.dut.base_dir > + return target_dir > + > + def d_con(self, cmd): > + _cmd =3D [cmd, '# ', 10] if isinstance(cmd, (str, unicode)) else= cmd > + return self.dut.send_expect(*_cmd) > + > + def d_a_con(self, cmd): > + _cmd =3D [cmd, '# ', 10] if isinstance(cmd, (str, unicode)) else= cmd > + return self.dut.alt_session.send_expect(*_cmd) > + > + def prepare_binary(self, name): > + example_dir =3D "examples/" + name > + out =3D self.dut.build_dpdk_apps('./' + example_dir) > + self.verify("Error" not in out, "Compilation error") > + self.verify("No such" not in out, "Compilation error") > + binary_dir =3D os.path.join(self.target_dir, example_dir, 'build= ') > + cmd =3D ["ls -F {0} | grep '*'".format(binary_dir), '# ', 5] > + exec_file =3D self.d_a_con(cmd) > + binary_file =3D os.path.join(binary_dir, exec_file[:-1]) > + return binary_file > + > + def get_cores_mask(self, cores_list): > + return dts_create_mask(cores_list) > + > + def set_pktgen_stream(self, txport, rxport, send_pkts, option): > + stream_ids =3D [] > + cnt =3D 0 > + for pkt in send_pkts: > + _option =3D deepcopy(option) > + _option['pcap'] =3D pkt > + stream_id =3D self.tester.pktgen.add_stream(txport, rxport, = pkt) > + self.tester.pktgen.config_stream(stream_id, _option) > + stream_ids.append(stream_id) > + # rxport -> txport > + stream_id =3D self.tester.pktgen.add_stream(rxport, txport, = pkt) > + self.tester.pktgen.config_stream(stream_id, _option) > + stream_ids.append(stream_id) > + cnt +=3D 1 > + return stream_ids > + > + def run_traffic(self, option): > + txport =3D self.tester.get_local_port(self.dut_ports[0]) > + rxport =3D self.tester.get_local_port(self.dut_ports[1]) > + stm_type =3D option.get('stm_types') > + rate_percent =3D option.get('rate', float(100)) > + duration =3D option.get('duration', 10) > + send_pkts =3D self.set_stream(stm_type) > + # set stream into pktgen > + option =3D { > + 'stream_config': { > + 'txmode': {}, > + 'transmit_mode': TRANSMIT_CONT, > + 'rate': rate_percent, } > + } > + stream_ids =3D self.set_pktgen_stream(txport, rxport, send_pkts,= option) > + # run traffic options > + traffic_opt =3D { > + 'method': 'throughput', > + 'duration': duration, } > + # run pktgen(ixia/trex) traffic > + result =3D self.tester.pktgen.measure(stream_ids, traffic_opt) > + > + return result > + > + def set_stream(self, stm_names=3DNone): > + # set streams for traffic > + pkt_configs =3D { > + 'UDP_1': { > + 'type': 'UDP', > + 'pkt_layers': {'ipv4': {'dst': '1.1.1.1'}, }}, > + } > + # create packet instance for send > + streams =3D [] > + for stm_name in stm_names: > + if stm_name not in pkt_configs.keys(): > + continue > + values =3D pkt_configs[stm_name] > + pkt_type =3D values.get('type') > + pkt_layers =3D values.get('pkt_layers') > + pkt =3D Packet(pkt_type=3Dpkt_type) > + for layer in pkt_layers.keys(): > + pkt.config_layer(layer, pkt_layers[layer]) > + streams.append(pkt.pktgen.pkt) > + > + return streams > + > + @property > + def empty_poll_options(self): > + table =3D { > + 'train': '1,0,0,', > + 'no-train': '0,350000,500000', } > + return table > + > + def init_l3fwd_power(self): > + self.l3fwd_power =3D self.prepare_binary('l3fwd-power') > + > + def start_l3fwd_power(self, core): > + train_mode =3D self.empty_poll_options.get(self.train_mode) > + option =3D (' ' > + '-c {core_mask} ' > + '-n {mem_channel} ' > + '-- ' > + '-p 0x1 ' > + '-P ' > + '--config=3D"(0,0,2)" ' > + '-l 10 -m 6 -h 1' > + '--empty-poll=3D"{empty-poll}" ' > + ).format(**{ > + 'core_mask': self.get_cores_mask(core), > + 'mem_channel': self.dut.get_memory_channels(), > + 'empty-poll': train_mode, }) > + prompt =3D 'L3FWD_POWER: entering main loop on lcore' > + cmd =3D [' '.join([self.l3fwd_power, option]), prompt, 60] > + self.d_con(cmd) > + > + def close_l3fwd_power(self): > + cmd =3D 'killall l3fwd-power' > + self.d_a_con(cmd) > + > + def init_query_script(self): > + script_content =3D textwrap.dedent(""" > + # $1: delay time before traffic start > + # $2: core number > + sleep 5 > + while : > + do > + sleep 1 > + cat /sys/devices/system/cpu/cpu$1/cpufreq/scaling_min_fr= eq >> > {0} > + cat /sys/devices/system/cpu/cpu$1/cpufreq/scaling_max_fr= eq >> > {1} > + done > + """).format(self.query_min_freq, self.query_max_freq) > + fileName =3D 'vm_power_core.sh' > + query_script =3D os.path.join(self.output_path, fileName) > + with open(query_script, 'wb') as fp: > + fp.write('#! /bin/sh' + os.linesep + script_content) > + self.dut.session.copy_file_to(query_script, self.target_dir) > + self.query_tool =3D ';'.join([ > + 'cd {}'.format(self.target_dir), > + 'chmod 777 {}'.format(fileName), > + './' + fileName]) > + > + def start_query(self, core): > + cmd =3D self.query_tool + ' {0} > /dev/null 2>&1 &'.format(core) > + self.d_a_con(cmd) > + > + def stop_query(self): > + cmd =3D 'pkill {}'.format(os.path.basename(self.query_tool)) > + self.d_a_con(cmd) > + self.dut.session.copy_file_from(self.query_min_freq, self.output= _path) > + self.dut.session.copy_file_from(self.query_max_freq, > + self.output_path) > + > + def get_sys_power_driver(self): > + drv_file =3D "/sys/devices/system/cpu/cpu0/cpufreq/scaling_drive= r" > + output =3D self.d_a_con('cat ' + drv_file) > + if not output: > + msg =3D 'unknown power driver' > + self.verify(False, msg) > + drv_name =3D output.splitlines()[0].strip() > + return drv_name > + > + def get_no_turbo_max(self): > + cmd =3D 'rdmsr -p 1 0x0CE -f 15:8 -d' > + output =3D self.d_a_con(cmd) > + freq =3D output.strip() + '00000' > + return int(freq) > + > + def check_core_freq_in_traffic(self, core_index, mode): > + ''' > + check the cores frequency when running traffic > + highest frequency[no_turbo_max]: > cur_min=3Dcur_max=3Dno_turbo_max > + ''' > + self.stop_query() > + freq =3D self.get_no_turbo_max() > + expected_freq =3D str(freq if mode =3D=3D self.HIGH else (freq -= 500000)) > + query_max_freq =3D os.path.join( > + self.output_path, os.path.basename(self.query_max_freq)) > + with open(query_max_freq, 'rb') as fp: > + content =3D fp.read() > + msg =3D 'max freq are not the same as highest frequency <{0}>' > + self.verify(expected_freq in content, msg.format(expected_freq)) > + query_min_freq =3D os.path.join( > + self.output_path, os.path.basename(self.query_min_freq)) > + with open(query_min_freq, 'rb') as fp: > + content =3D fp.read() > + msg =3D 'min freq are not the same as highest frequency <{0}>' > + self.verify(expected_freq in content, msg.format(expected_freq)) > + msg =3D 'core <{0}>: max freq/min_freq/expected freq<{1}> are th= e > same' > + self.logger.info(msg.format(core_index, expected_freq)) > + > + def check_no_train(self): > + output =3D self.dut.get_session_output(timeout=3D2) > + msg =3D 'training steps should not be executed' > + self.verify('POWER: Training is Complete' not in output, msg) > + > + def verify_train_mode(self): > + except_content =3D None > + # begin run vm power policy testing > + try: > + self.start_l3fwd_power(self.check_core) > + if self.train_mode =3D=3D self.NOTRAIN: > + self.check_no_train() > + else: > + # Injected Rate(64B, dst_ip=3D1.1.1.1): > + # 10G -> 0.1G -> 10G -> 0.1G -> 10G -> 0.1G > + check_item =3D [ > + [100, self.HIGH], > + [1, self.MED], > + [100, self.HIGH], > + [1, self.MED], > + [100, self.HIGH], > + [1, self.MED], ] > + msg =3D '{0} begin test mode <{1}> with traffic rate per= cent {2}%' > + for rate, mode in check_item: > + self.logger.info(msg.format(self.train_mode, mode, r= ate)) > + self.start_query(self.check_core[1]) > + info =3D { > + 'stm_types': ['UDP_1'], > + 'rate': rate} > + # run traffic > + self.run_traffic(info) > + time.sleep(2) > + self.stop_query() > + # check test result > + self.check_core_freq_in_traffic(self.check_core[1], = mode) > + except Exception as e: > + self.logger.error(traceback.format_exc()) > + except_content =3D e > + finally: > + self.close_l3fwd_power() > + > + # check verify result > + if except_content: > + raise Exception(except_content) > + else: > + msg =3D "test <{0}> successful !!!".format(self.train_mode) > + self.logger.info(msg) > + > + def verify_power_driver(self): > + expected_drv =3D 'intel_pstate' > + power_drv =3D self.get_sys_power_driver() > + msg =3D "{0} should work with {1} driver on DUT".format( > + self.suite_name, expected_drv) > + self.verify(power_drv =3D=3D expected_drv, msg) > + > + def preset_test_environment(self): > + self.check_core =3D [1, 2] > + # modprobe msr module to let the application can get the CPU HW = info > + self.d_a_con('modprobe msr') > + # init binary > + self.init_l3fwd_power() > + self.init_query_script() > + # > + # Test cases. > + # > + > + def set_up_all(self): > + """ > + Run at the start of each test suite. > + """ > + self.verify_power_driver() > + self.dut_ports =3D self.dut.get_ports(self.nic) > + self.verify(len(self.dut_ports) >=3D 2, "Not enough ports") > + # prepare testing environment > + self.preset_test_environment() > + > + def tear_down_all(self): > + """ > + Run after each test suite. > + """ > + pass > + > + def set_up(self): > + """ > + Run before each test case. > + """ > + pass > + > + def tear_down(self): > + """ > + Run after each test case. > + """ > + self.dut.kill_all() > + > + def test_perf_basic_train_mode(self): > + """ > + Set Branch-Ratio Rate by User > + """ > + self.train_mode =3D self.TRAIN > + self.verify_train_mode() > + > + def test_perf_no_training_mode(self): > + """ > + Set Branch-Ratio Rate by User > + """ > + self.train_mode =3D self.NOTRAIN > + self.verify_train_mode() > -- > 2.21.0