From patchwork Wed Aug 5 13:52:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Owen Hilyard X-Patchwork-Id: 75209 Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 87ADFA053A; Wed, 5 Aug 2020 15:52:44 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 41CD71B203; Wed, 5 Aug 2020 15:52:44 +0200 (CEST) Received: from mail-lj1-f193.google.com (mail-lj1-f193.google.com [209.85.208.193]) by dpdk.org (Postfix) with ESMTP id 636C72C28 for ; Wed, 5 Aug 2020 15:52:43 +0200 (CEST) Received: by mail-lj1-f193.google.com with SMTP id t23so24259746ljc.3 for ; Wed, 05 Aug 2020 06:52:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iol.unh.edu; s=unh-iol; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=5dRKQykwgxamftlQRJCSfBRMeUqA97Pz/Vp9wJIDfik=; b=QxHOfmIlq6/JgjVNKnV3YG+Mv+SSGfcPJRStWFB/tOe99EYs5fAXZouKscpnVUSyUq IdSSTfdOtg0b8xTWOMRLQ7boY/tA1eq5JgVk7ybxifx+WUO3o3mSEFlfhIhfm4GFIgbn S5VCQu1a3j9DKp+VgcI+I88fZ3mRjVUIxZAIg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=5dRKQykwgxamftlQRJCSfBRMeUqA97Pz/Vp9wJIDfik=; b=Co2fK2jq++jPdeSRP36wlsaOCIzEWv/ox3KRvMTBL5CDdN4AVdCA9ZqmXUx1tjRZ/D LtcOzQR7weyPCl89TmclD8RuYfgTI8MwK3tfoUiABFE8JCyVp/C2wUDxswDfoRWGAosn QRq0J1RCv9kDJe5HaQC1s1MaqSZDz+ZOmaSKpJttHAvvLGqAZbIZiEUfOAijub7DlVEw OQe12EYhLCYe+P9QI7eZ+dgUlp23BXCSdtPVtOU4BNZ+54rR6ZGpkuEPqOXy9vuXm7+Q NT8HEuohomKW7pOJbnpFk5hDU1JikYSZiTvmXITTvzCsZWkkbdEKlrnu7On02cy9dx08 vj+Q== X-Gm-Message-State: AOAM53219VJwl6JwuaZadwDP3jKoq1ZkuyWxlSzsVQRmVJub6DH29uGh NET/8UPVtUqFVIqqhLnxJPPGgWv0whggMwX+Ilo4/MULWBbgqfpFjfctdegtNcSef0H/3pcFS6q Uo19grqZtuEM6qeD7bjTN8M4fA0eLY+xPcf0re9rt6Vc5AH/YnioEeHt7JA== X-Google-Smtp-Source: ABdhPJwOhGvsalkHga4rqrE5fjYHgnB/8nyYl4edzrlkGdXLFaGpGv28Jqu8WSpYS9+pGwxaL3Nokw== X-Received: by 2002:a2e:8e28:: with SMTP id r8mr1381422ljk.290.1596635561371; Wed, 05 Aug 2020 06:52:41 -0700 (PDT) Received: from ohilyard-Alienware-m17.iol.unh.edu ([2606:4100:3880:1254::1033]) by smtp.gmail.com with ESMTPSA id t18sm1136103lfb.69.2020.08.05.06.52.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Aug 2020 06:52:40 -0700 (PDT) From: Owen Hilyard To: dts@dpdk.org Cc: nhorman@tuxdriver.com, jerinj@marvell.com, skori@marvell.com, anatoly.burakov@intel.com, ferruh.yigit@intel.com, lijuan.tu@intel.com, lylavoie@iol.unh.edu, Owen Hilyard Date: Wed, 5 Aug 2020 09:52:30 -0400 Message-Id: <20200805135231.1579592-1-ohilyard@iol.unh.edu> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 Subject: [dts] [PATCH] linux kernel modules: add tests 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" add vfio-pci test suite add igb_uio test suite add uio_pci_generic test suite add test suite documentation add new setting UNPRIVILEGED_USERNAME add check for file existence when getting dpdk pids dpdk processes run in memory will not create the expected files add return to DPDKdut.bind_interfaces_linux to allow for error checking Signed-off-by: Owen Hilyard --- framework/crb.py | 40 +++--- framework/project_dpdk.py | 2 +- framework/settings.py | 2 + test_plans/linux_modules_test_plan.rst | 158 ++++++++++++++++++++++ tests/TestSuite_linux_modules.py | 173 +++++++++++++++++++++++++ 5 files changed, 356 insertions(+), 19 deletions(-) create mode 100644 test_plans/linux_modules_test_plan.rst create mode 100644 tests/TestSuite_linux_modules.py diff --git a/framework/crb.py b/framework/crb.py index a47de2c..d456fab 100644 --- a/framework/crb.py +++ b/framework/crb.py @@ -499,27 +499,31 @@ class Crb(object): pids = [] pid_reg = r'p(\d+)' for config_file in file_directorys: - cmd = 'lsof -Fp %s' % config_file - out = self.send_expect(cmd, "# ", 20, alt_session) - if len(out): - lines = out.split('\r\n') - for line in lines: - m = re.match(pid_reg, line) - if m: - pids.append(m.group(1)) - for pid in pids: - self.send_expect('kill -9 %s' % pid, '# ', 20, alt_session) - self.get_session_output(timeout=2) + # Covers case where the process is run as a unprivileged user and does not generate the file + if os.path.isfile(config_file): + cmd = 'lsof -Fp %s' % config_file + out = self.send_expect(cmd, "# ", 20, alt_session) + if len(out): + lines = out.split('\r\n') + for line in lines: + m = re.match(pid_reg, line) + if m: + pids.append(m.group(1)) + for pid in pids: + self.send_expect('kill -9 %s' % pid, '# ', 20, alt_session) + self.get_session_output(timeout=2) hugepage_info = ['/var/run/dpdk/%s/hugepage_info' % file_prefix for file_prefix in prefix_list] for hugepage in hugepage_info: - cmd = 'lsof -Fp %s' % hugepage - out = self.send_expect(cmd, "# ", 20, alt_session) - if len(out) and "No such file or directory" not in out: - self.logger.warning("There are some dpdk process not free hugepage") - self.logger.warning("**************************************") - self.logger.warning(out) - self.logger.warning("**************************************") + # Covers case where the process is run as a unprivileged user and does not generate the file + if os.path.isfile(hugepage): + cmd = 'lsof -Fp %s' % hugepage + out = self.send_expect(cmd, "# ", 20, alt_session) + if len(out) and "No such file or directory" not in out: + self.logger.warning("There are some dpdk process not free hugepage") + self.logger.warning("**************************************") + self.logger.warning(out) + self.logger.warning("**************************************") # remove directory directorys = ['/var/run/dpdk/%s' % file_prefix for file_prefix in prefix_list] diff --git a/framework/project_dpdk.py b/framework/project_dpdk.py index 040d070..03ace46 100644 --- a/framework/project_dpdk.py +++ b/framework/project_dpdk.py @@ -434,7 +434,7 @@ class DPDKdut(Dut): current_nic += 1 bind_script_path = self.get_dpdk_bind_script() - self.send_expect('%s --force %s' % (bind_script_path, binding_list), '# ') + return self.send_expect('%s --force %s' % (bind_script_path, binding_list), '# ') def unbind_interfaces_linux(self, nics_to_bind=None): """ diff --git a/framework/settings.py b/framework/settings.py index f91452d..850fa89 100644 --- a/framework/settings.py +++ b/framework/settings.py @@ -197,6 +197,8 @@ SCAPY2IXIA = [ USERNAME = 'root' +# A user used to test functionality for a non-root user +UNPRIVILEGED_USERNAME = 'dtsunprivilegedtester' """ Helpful header sizes. diff --git a/test_plans/linux_modules_test_plan.rst b/test_plans/linux_modules_test_plan.rst new file mode 100644 index 0000000..c7bb2d0 --- /dev/null +++ b/test_plans/linux_modules_test_plan.rst @@ -0,0 +1,158 @@ +.. # BSD LICENSE + # + # Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + # Copyright © 2018[, 2019] The University of New Hampshire. 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. + +================== +Linux Driver Tests +================== + +This file contains multiple test suites to avoid a single unsupported +kernel module causing the entire test suite to fail. These test suites +cover a variety of kernel modules and are built to check their function +both in use as root and as an unprivileged user. All of the test suites +run the same tests. In the documentation for test cases, will +represent the name of the module being tested. will +represent the character interface under /dev/ for the interface. + +Prerequisites +============= + +There are two prerequisites. First, all of the drivers that you wish +to test must be compiled and installed so that they are available through +modprobe. Secondly, there should be a user on the dut which has the same +password as the primary account for dts. This account will be used as the +unprivileged user, but it still should have permission to lock at least +1 GiB of memory to ensure that it can lock all of the process memory. + +Test Suites +=========== + +There is 1 test suite per module, the modules are as follows: + + * VFIO-PCI + * UIO PCI GENERIC + * IGB UIO + +Test Case: TX RX +==================== +This test case runs as root and is designed to check the basic functioning +of the module. It checks whether packets can be sent and received. + +Remove old module :: + + # rmmod + +Add the new one :: + + # modprobe + +Bind the interface to the driver :: + + # usertools/dpdk-devbind.py --force --bind= xxxx:xx:xx.x + +Start testpmd in a loop configuration :: + + # x86_64-native-linux-gcc/app/testpmd -l 1,2 -n 4 -w xxxx:xx:xx.x \ + -- -i --port-topology=loop + +Start packet forwarding :: + + testpmd> start + +Start a packet capture on the tester:: + + # tcpdump -i (interface) ether src (tester mac address) + +Send some packets to the dut and check that they are properly sent back into +the packet capture on the tester. + +Test Case: TX RX Userspace +==================== +This test case runs as the unprivileged user and is designed to check the +basic functioning of the module. It checks whether packets can be sent +and received when running dpdk applications as a normal user. # means +that a command is run as root. $ means that a command is run as the user. +The igb_uio module requires that the iova mode is in virtual address mode, +which can be done by adding the flag "--iova-mode va" as an eal option to +testpmd. + +Remove old module :: + + # rmmod + +Add the new one :: + + # modprobe + +Bind the interface to the driver :: + + # usertools/dpdk-devbind.py --force --bind= xxxx:xx:xx.x + +Grant permissions for all users to access the new character device :: + + # setfacl -m u:dtsunprivilegedtester:rwx + +Start testpmd in a loop configuration :: + + $ x86_64-native-linux-gcc/app/testpmd -l 1,2 -n 4 -w xxxx:xx:xx.x --in-memory \ + -- -i --port-topology=loop + +Start packet forwarding :: + + testpmd> start + +Start a packet capture on the tester:: + + # tcpdump -i (interface) ether src (tester mac address) + +Send some packets to the dut and check that they are properly sent back into +the packet capture on the tester. + +Test Case: Hello World +==================== +This is a more basic test of functionality as a normal user than the +TX RX Userspace case. It simply involves running a short, hello-world-like +program on each core before shutting down. # means that a command is run +as root. $ means that a command is run as the user. The igb_uio module +requires that the iova mode is in virtual address mode, which can be done +by adding the flag "--iova-mode va" as an eal option to the hello world +application. + +Compile the application :: + + # cd $RTE_SDK/examples/helloworld && make + +Run the application :: + + $ $RTE_SDK/examples/helloworld/build/helloworld-shared --in-memory + +Check for any error states or reported errors. + diff --git a/tests/TestSuite_linux_modules.py b/tests/TestSuite_linux_modules.py new file mode 100644 index 0000000..7cb479d --- /dev/null +++ b/tests/TestSuite_linux_modules.py @@ -0,0 +1,173 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. +# Copyright © 2018[, 2019] The University of New Hampshire. 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. +Linux Kernel Modules example. +""" +import os +import time + +from pmd_output import PmdOutput +from test_case import TestCase + +from framework import settings + +ETHER_HEADER_LEN = 18 +IP_HEADER_LEN = 20 +ETHER_STANDARD_MTU = 1518 +ETHER_JUMBO_FRAME_MTU = 9000 + + +# A class like this is used because doing inheritance normally breaks the test case discovery process +class LinuxModulesHelperMethods: + driver: str + dev_interface: str + additional_eal_options: str + + def set_up_all(self): + """ + Prerequisite steps for each test suit. + """ + self.dut_ports = self.dut.get_ports() + self.pmdout = PmdOutput(self.dut) + pci_address = self.dut.ports_info[self.dut_ports[0]]['pci'] + self.old_driver = settings.get_nic_driver(pci_address) + out = self.dut.bind_interfaces_linux(driver=self.driver) + self.verify("bind failed" not in out, f"Failed to bind {self.driver}") + self.verify("not loaded" not in out, f"{self.driver} was not loaded") + + def send_scapy_packet(self, port_id: int, packet: str): + itf = self.tester.get_interface(port_id) + + self.tester.scapy_foreground() + self.tester.scapy_append(f'sendp({packet}, iface="{itf}")') + return self.tester.scapy_execute() + + def tear_down(self): + self.dut.kill_all() + + def run_example_program_in_userspace(self, directory: str, program: str): + """ + A function to run a given example program as an unprivileged user. + @param directory: The directory under examples where the app is + @param program: the name of the binary to run + """ + out: str = self.dut.build_dpdk_apps(f"$RTE_SDK/examples/{directory}") + self.verify("Error" not in out, "Compilation error") + self.verify("No such" not in out, "Compilation error") + + program_build_location = f"$RTE_SDK/examples/{directory}/build/{program}" + program_user_location = f"/tmp/dut/bin/{program}" + + self.dut.send_expect(f"chmod +x {program_build_location}", "# ") + self.dut.send_expect("mkdir -p /tmp/dut/bin/", "# ") + user_home_dir = self.dut.send_expect(f"cp {program_build_location} {program_user_location}", "# ") + + self.dut.alt_session.send_expect(f"su {settings.UNPRIVILEGED_USERNAME}", "# ") + self.dut.alt_session.send_expect(f"{program_user_location} --in-memory {self.additional_eal_options}", "# ") + out: str = self.dut.alt_session.send_expect("echo $?", "# ") + self.dut.alt_session.send_expect("exit", "# ") # Return to root session + self.verify(out.strip() == "0", f"{program} exited in an error state") + + def tx_rx_test_helper(self, pmdout, param="", eal_param=""): + pmdout.start_testpmd("Default", param=f"--port-topology=loop {param}", + eal_param=f"{eal_param} {self.additional_eal_options}") + pmdout.execute_cmd("start") + dut_mac = self.dut.get_mac_address(self.dut_ports[0]) + tester_mac = self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0])) + iface = self.tester.get_interface(self.dut_ports[0]) + pcap_path: str = f"/tmp/tester/test-{self.driver}.pcap" + self.tester.send_expect(f"tcpdump -i {iface} -w /tmp/tester/test-{self.driver}.pcap ether src {tester_mac} &", + "# ") + self.tester.send_expect(f"TCPDUMP_PID=$!", "# ") + self.send_scapy_packet(self.dut_ports[0], + f"[Ether(dst='{dut_mac}', src='{tester_mac}')/IP()/TCP()/('a') for i in range(20)]") + time.sleep(.1) + self.tester.send_expect("kill -SIGINT $TCPDUMP_PID", "# ") + os.system(f"mkdir -p {settings.FOLDERS['Output']}/tmp/pcap/") + self.tester.session.copy_file_from(pcap_path, dst=os.path.join(settings.FOLDERS['Output'], "tmp/pcap/")) + out: str = self.tester.send_expect(f"tcpdump -r /tmp/tester/test-{self.driver}.pcap", "# ") + self.verify(len(out.splitlines()) >= 20, "Not all packets were received by the tester.") + pmdout.quit() + + # + # + # + # Test cases. + # + + def tear_down_all(self): + """ + When the case of this test suite finished, the environment should + clear up. + """ + self.dut.bind_interfaces_linux(driver=self.old_driver) + self.dut.kill_all() + + def test_tx_rx(self): + """ + Preforms the testing that needs to be done as root. + @param driver: The driver to test + """ + self.tx_rx_test_helper(self.pmdout) + + def test_helloworld(self): + self.run_example_program_in_userspace("helloworld", "helloworld-shared") + + def test_tx_rx_userspace(self): + app_path = self.dut.apps_name['test-pmd'] + self.dut.send_expect(f"chmod +rx {app_path}", "#") + path = self.dut.send_expect("pwd", "#") + self.dut.alt_session.send_expect(f"su {settings.UNPRIVILEGED_USERNAME}", "#") + self.dut.alt_session.send_expect(f"cd {path}", "#") + self.dut.send_expect(f"setfacl -m u:{settings.UNPRIVILEGED_USERNAME}:rwx {self.dev_interface}", "#") + self.tx_rx_test_helper(PmdOutput(self.dut, session=self.dut.alt_session), eal_param="--in-memory") + self.dut.alt_session.send_expect(f"exit", "#") + + +class TestVfio(LinuxModulesHelperMethods, TestCase): + driver = "vfio-pci" + dev_interface = "/dev/vfio/*" + additional_eal_options = "" + + +class TestIgbuio(LinuxModulesHelperMethods, TestCase): + driver = "igb_uio" + dev_interface = "/dev/uio*" + additional_eal_options = "--iova-mode va" + + +class TestUioPciGeneric(LinuxModulesHelperMethods, TestCase): + driver = "uio_pci_generic" + dev_interface = "/dev/uio*" + additional_eal_options = ""