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 = "" -- 2.25.1