From f871f9bacde8cdeaa7a1f6200ff39c79114c4ef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=ADcolas=20F=2E=20R=2E=20A=2E=20Prado?= Date: Thu, 13 Jun 2024 11:14:51 -0400 Subject: kselftest: devices: Allow specifying boards directory through parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for a --boards-dir parameter through which the directory in which the board files will be searched for can be specified. The 'boards' subdirectory is still used as default when the parameter is not specified. This allows more easily running the test with board files supplied by an external repository like https://github.com/kernelci/platform-test-parameters. Signed-off-by: Nícolas F. R. A. Prado Link: https://lore.kernel.org/r/20240613-kselftest-discoverable-probe-mt8195-kci-v1-1-7b396a9b032d@collabora.com Signed-off-by: Greg Kroah-Hartman --- .../testing/selftests/devices/test_discoverable_devices.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/devices/test_discoverable_devices.py b/tools/testing/selftests/devices/test_discoverable_devices.py index fbae8deb593d..19f28ea774f4 100755 --- a/tools/testing/selftests/devices/test_discoverable_devices.py +++ b/tools/testing/selftests/devices/test_discoverable_devices.py @@ -14,6 +14,7 @@ # the description and examples of the file structure and vocabulary. # +import argparse import glob import ksft import os @@ -296,14 +297,24 @@ def run_test(yaml_file): parse_device_tree_node(device_tree) +parser = argparse.ArgumentParser() +parser.add_argument( + "--boards-dir", default="boards", help="Directory containing the board YAML files" +) +args = parser.parse_args() + find_pci_controller_dirs() find_usb_controller_dirs() ksft.print_header() +if not os.path.exists(args.boards_dir): + ksft.print_msg(f"Boards directory '{args.boards_dir}' doesn't exist") + ksft.exit_fail() + board_file = "" for board_filename in get_board_filenames(): - full_board_filename = os.path.join("boards", board_filename + ".yaml") + full_board_filename = os.path.join(args.boards_dir, board_filename + ".yaml") if os.path.exists(full_board_filename): board_file = full_board_filename -- cgit v1.2.3 From 819984a0dd3606b7c46fe156cd56a0dc0d604788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=ADcolas=20F=2E=20R=2E=20A=2E=20Prado?= Date: Thu, 13 Jun 2024 11:14:52 -0400 Subject: kselftest: devices: Add of-fullname-regex property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new 'of-fullname-regex' property that takes a regular expression and matches against the OF_FULLNAME property. It allows matching controllers that don't have a unique DT address across sibling controllers, and thus dt-mmio can't be used. One particular example of where this is needed is on MT8195 which has multiple USB controllers described by two level deep nodes and using the ranges property: ssusb2: usb@112a1000 { reg = <0 0x112a1000 0 0x2dff>, <0 0x112a3e00 0 0x0100>; ranges = <0 0 0 0x112a0000 0 0x3f00>; xhci2: usb@0 { Signed-off-by: Nícolas F. R. A. Prado Link: https://lore.kernel.org/r/20240613-kselftest-discoverable-probe-mt8195-kci-v1-2-7b396a9b032d@collabora.com Signed-off-by: Greg Kroah-Hartman --- .../selftests/devices/boards/google,spherion.yaml | 4 ++++ .../selftests/devices/test_discoverable_devices.py | 24 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'tools/testing') diff --git a/tools/testing/selftests/devices/boards/google,spherion.yaml b/tools/testing/selftests/devices/boards/google,spherion.yaml index 17157ecd8c14..3ea843324797 100644 --- a/tools/testing/selftests/devices/boards/google,spherion.yaml +++ b/tools/testing/selftests/devices/boards/google,spherion.yaml @@ -11,6 +11,10 @@ # this, several optional keys can be used: # - dt-mmio: identify the MMIO address of the controller as defined in the # Devicetree. +# - of-fullname-regex: regular expression to match against the OF_FULLNAME +# property. Useful when the controller's address is not unique across other +# sibling controllers. In this case, dt-mmio can't be used, and this property +# allows the matching to include parent nodes as well to make it unique. # - usb-version: for USB controllers to differentiate between USB3 and USB2 # buses sharing the same controller. # - acpi-uid: _UID property of the controller as supplied by the ACPI. Useful to diff --git a/tools/testing/selftests/devices/test_discoverable_devices.py b/tools/testing/selftests/devices/test_discoverable_devices.py index 19f28ea774f4..8f2200540a1f 100755 --- a/tools/testing/selftests/devices/test_discoverable_devices.py +++ b/tools/testing/selftests/devices/test_discoverable_devices.py @@ -64,6 +64,22 @@ def get_dt_mmio(sysfs_dev_dir): sysfs_dev_dir = os.path.dirname(sysfs_dev_dir) +def get_of_fullname(sysfs_dev_dir): + re_of_fullname = re.compile("OF_FULLNAME=(.*)") + of_full_name = None + + # PCI controllers' sysfs don't have an of_node, so have to read it from the + # parent + while not of_full_name: + try: + with open(os.path.join(sysfs_dev_dir, "uevent")) as f: + of_fullname = re_of_fullname.search(f.read()).group(1) + return of_fullname + except: + pass + sysfs_dev_dir = os.path.dirname(sysfs_dev_dir) + + def get_acpi_uid(sysfs_dev_dir): with open(os.path.join(sysfs_dev_dir, "firmware_node", "uid")) as f: return f.read() @@ -97,6 +113,11 @@ def find_controller_in_sysfs(controller, parent_sysfs=None): if str(controller["dt-mmio"]) != get_dt_mmio(c): continue + if controller.get("of-fullname-regex"): + re_of_fullname = re.compile(str(controller["of-fullname-regex"])) + if not re_of_fullname.match(get_of_fullname(c)): + continue + if controller.get("usb-version"): if controller["usb-version"] != get_usb_version(c): continue @@ -195,6 +216,9 @@ def generate_pathname(device): if device.get("dt-mmio"): pathname += "@" + str(device["dt-mmio"]) + if device.get("of-fullname-regex"): + pathname += "-" + str(device["of-fullname-regex"]) + if device.get("name"): pathname = pathname + "/" + device["name"] -- cgit v1.2.3 From 0debb20c5c812f8750c20c3406bc94a1e8ea4742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=ADcolas=20F=2E=20R=2E=20A=2E=20Prado?= Date: Fri, 5 Jul 2024 19:29:54 -0400 Subject: kselftest: devices: Move discoverable devices test to subdirectory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the discoverable devices test to a subdirectory to allow other related tests to be added to the devices directory. Signed-off-by: Nícolas F. R. A. Prado Acked-by: Shuah Khan Acked-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240705-dev-err-log-selftest-v2-1-163b9cd7b3c1@collabora.com Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/Makefile | 2 +- tools/testing/selftests/devices/Makefile | 4 - .../devices/boards/Dell Inc.,XPS 13 9300.yaml | 40 --- .../selftests/devices/boards/google,spherion.yaml | 54 ---- tools/testing/selftests/devices/ksft.py | 90 ------ tools/testing/selftests/devices/probe/Makefile | 4 + .../probe/boards/Dell Inc.,XPS 13 9300.yaml | 40 +++ .../devices/probe/boards/google,spherion.yaml | 54 ++++ tools/testing/selftests/devices/probe/ksft.py | 90 ++++++ .../devices/probe/test_discoverable_devices.py | 353 +++++++++++++++++++++ .../selftests/devices/test_discoverable_devices.py | 353 --------------------- 11 files changed, 542 insertions(+), 542 deletions(-) delete mode 100644 tools/testing/selftests/devices/Makefile delete mode 100644 tools/testing/selftests/devices/boards/Dell Inc.,XPS 13 9300.yaml delete mode 100644 tools/testing/selftests/devices/boards/google,spherion.yaml delete mode 100644 tools/testing/selftests/devices/ksft.py create mode 100644 tools/testing/selftests/devices/probe/Makefile create mode 100644 tools/testing/selftests/devices/probe/boards/Dell Inc.,XPS 13 9300.yaml create mode 100644 tools/testing/selftests/devices/probe/boards/google,spherion.yaml create mode 100644 tools/testing/selftests/devices/probe/ksft.py create mode 100755 tools/testing/selftests/devices/probe/test_discoverable_devices.py delete mode 100755 tools/testing/selftests/devices/test_discoverable_devices.py (limited to 'tools/testing') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 9039f3709aff..37214201d974 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -13,7 +13,7 @@ TARGETS += core TARGETS += cpufreq TARGETS += cpu-hotplug TARGETS += damon -TARGETS += devices +TARGETS += devices/probe TARGETS += dmabuf-heaps TARGETS += drivers/dma-buf TARGETS += drivers/s390x/uvdevice diff --git a/tools/testing/selftests/devices/Makefile b/tools/testing/selftests/devices/Makefile deleted file mode 100644 index ca29249b30c3..000000000000 --- a/tools/testing/selftests/devices/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -TEST_PROGS := test_discoverable_devices.py -TEST_FILES := boards ksft.py - -include ../lib.mk diff --git a/tools/testing/selftests/devices/boards/Dell Inc.,XPS 13 9300.yaml b/tools/testing/selftests/devices/boards/Dell Inc.,XPS 13 9300.yaml deleted file mode 100644 index ff932eb19f0b..000000000000 --- a/tools/testing/selftests/devices/boards/Dell Inc.,XPS 13 9300.yaml +++ /dev/null @@ -1,40 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# This is the device definition for the XPS 13 9300. -# The filename "Dell Inc.,XPS 13 9300" was chosen following the format -# "Vendor,Product", where Vendor comes from -# /sys/devices/virtual/dmi/id/sys_vendor, and Product comes from -# /sys/devices/virtual/dmi/id/product_name. -# -# See google,spherion.yaml for more information. -# -- type: pci-controller - # This machine has a single PCI host controller so it's valid to not have any - # key to identify the controller. If it had more than one controller, the UID - # of the controller from ACPI could be used to distinguish as follows: - #acpi-uid: 0 - devices: - - path: 14.0 - type: usb-controller - usb-version: 2 - devices: - - path: 9 - name: camera - interfaces: [0, 1, 2, 3] - - path: 10 - name: bluetooth - interfaces: [0, 1] - - path: 2.0 - name: gpu - - path: 4.0 - name: thermal - - path: 12.0 - name: sensors - - path: 14.3 - name: wifi - - path: 1d.0/0.0 - name: ssd - - path: 1d.7/0.0 - name: sdcard-reader - - path: 1f.3 - name: audio diff --git a/tools/testing/selftests/devices/boards/google,spherion.yaml b/tools/testing/selftests/devices/boards/google,spherion.yaml deleted file mode 100644 index 3ea843324797..000000000000 --- a/tools/testing/selftests/devices/boards/google,spherion.yaml +++ /dev/null @@ -1,54 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# This is the device definition for the Google Spherion Chromebook. -# The filename "google,spherion" comes from the Devicetree compatible, so this -# file will be automatically used when the test is run on that machine. -# -# The top-level is a list of controllers, either for USB or PCI(e). -# Every controller needs to have a 'type' key set to either 'usb-controller' or -# 'pci-controller'. -# Every controller needs to be uniquely identified on the platform. To achieve -# this, several optional keys can be used: -# - dt-mmio: identify the MMIO address of the controller as defined in the -# Devicetree. -# - of-fullname-regex: regular expression to match against the OF_FULLNAME -# property. Useful when the controller's address is not unique across other -# sibling controllers. In this case, dt-mmio can't be used, and this property -# allows the matching to include parent nodes as well to make it unique. -# - usb-version: for USB controllers to differentiate between USB3 and USB2 -# buses sharing the same controller. -# - acpi-uid: _UID property of the controller as supplied by the ACPI. Useful to -# distinguish between multiple PCI host controllers. -# -# The 'devices' key defines a list of devices that are accessible under that -# controller. A device might be a leaf device or another controller (see -# 'Dell Inc.,XPS 13 9300.yaml'). -# -# The 'path' key is needed for every child device (that is, not top-level) to -# define how to reach this device from the parent controller. For USB devices it -# follows the format \d(.\d)* and denotes the port in the hub at each level in -# the USB topology. For PCI devices it follows the format \d.\d(/\d.\d)* -# denoting the device (identified by device-function pair) at each level in the -# PCI topology. -# -# The 'name' key is used in the leaf devices to name the device for clarity in -# the test output. -# -# For USB leaf devices, the 'interfaces' key should contain a list of the -# interfaces in that device that should be bound to a driver. -# -- type: usb-controller - dt-mmio: 11200000 - usb-version: 2 - devices: - - path: 1.4.1 - interfaces: [0, 1] - name: camera - - path: 1.4.2 - interfaces: [0, 1] - name: bluetooth -- type: pci-controller - dt-mmio: 11230000 - devices: - - path: 0.0/0.0 - name: wifi diff --git a/tools/testing/selftests/devices/ksft.py b/tools/testing/selftests/devices/ksft.py deleted file mode 100644 index cd89fb2bc10e..000000000000 --- a/tools/testing/selftests/devices/ksft.py +++ /dev/null @@ -1,90 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Copyright (c) 2023 Collabora Ltd -# -# Kselftest helpers for outputting in KTAP format. Based on kselftest.h. -# - -import sys - -ksft_cnt = {"pass": 0, "fail": 0, "skip": 0} -ksft_num_tests = 0 -ksft_test_number = 1 - -KSFT_PASS = 0 -KSFT_FAIL = 1 -KSFT_SKIP = 4 - - -def print_header(): - print("TAP version 13") - - -def set_plan(num_tests): - global ksft_num_tests - ksft_num_tests = num_tests - print("1..{}".format(num_tests)) - - -def print_cnts(): - print( - f"# Totals: pass:{ksft_cnt['pass']} fail:{ksft_cnt['fail']} xfail:0 xpass:0 skip:{ksft_cnt['skip']} error:0" - ) - - -def print_msg(msg): - print(f"# {msg}") - - -def _test_print(result, description, directive=None): - if directive: - directive_str = f"# {directive}" - else: - directive_str = "" - - global ksft_test_number - print(f"{result} {ksft_test_number} {description} {directive_str}") - ksft_test_number += 1 - - -def test_result_pass(description): - _test_print("ok", description) - ksft_cnt["pass"] += 1 - - -def test_result_fail(description): - _test_print("not ok", description) - ksft_cnt["fail"] += 1 - - -def test_result_skip(description): - _test_print("ok", description, "SKIP") - ksft_cnt["skip"] += 1 - - -def test_result(condition, description=""): - if condition: - test_result_pass(description) - else: - test_result_fail(description) - - -def finished(): - if ksft_cnt["pass"] == ksft_num_tests: - exit_code = KSFT_PASS - else: - exit_code = KSFT_FAIL - - print_cnts() - - sys.exit(exit_code) - - -def exit_fail(): - print_cnts() - sys.exit(KSFT_FAIL) - - -def exit_pass(): - print_cnts() - sys.exit(KSFT_PASS) diff --git a/tools/testing/selftests/devices/probe/Makefile b/tools/testing/selftests/devices/probe/Makefile new file mode 100644 index 000000000000..7a6eaa031cfe --- /dev/null +++ b/tools/testing/selftests/devices/probe/Makefile @@ -0,0 +1,4 @@ +TEST_PROGS := test_discoverable_devices.py +TEST_FILES := boards ksft.py + +include ../../lib.mk diff --git a/tools/testing/selftests/devices/probe/boards/Dell Inc.,XPS 13 9300.yaml b/tools/testing/selftests/devices/probe/boards/Dell Inc.,XPS 13 9300.yaml new file mode 100644 index 000000000000..ff932eb19f0b --- /dev/null +++ b/tools/testing/selftests/devices/probe/boards/Dell Inc.,XPS 13 9300.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# This is the device definition for the XPS 13 9300. +# The filename "Dell Inc.,XPS 13 9300" was chosen following the format +# "Vendor,Product", where Vendor comes from +# /sys/devices/virtual/dmi/id/sys_vendor, and Product comes from +# /sys/devices/virtual/dmi/id/product_name. +# +# See google,spherion.yaml for more information. +# +- type: pci-controller + # This machine has a single PCI host controller so it's valid to not have any + # key to identify the controller. If it had more than one controller, the UID + # of the controller from ACPI could be used to distinguish as follows: + #acpi-uid: 0 + devices: + - path: 14.0 + type: usb-controller + usb-version: 2 + devices: + - path: 9 + name: camera + interfaces: [0, 1, 2, 3] + - path: 10 + name: bluetooth + interfaces: [0, 1] + - path: 2.0 + name: gpu + - path: 4.0 + name: thermal + - path: 12.0 + name: sensors + - path: 14.3 + name: wifi + - path: 1d.0/0.0 + name: ssd + - path: 1d.7/0.0 + name: sdcard-reader + - path: 1f.3 + name: audio diff --git a/tools/testing/selftests/devices/probe/boards/google,spherion.yaml b/tools/testing/selftests/devices/probe/boards/google,spherion.yaml new file mode 100644 index 000000000000..3ea843324797 --- /dev/null +++ b/tools/testing/selftests/devices/probe/boards/google,spherion.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# This is the device definition for the Google Spherion Chromebook. +# The filename "google,spherion" comes from the Devicetree compatible, so this +# file will be automatically used when the test is run on that machine. +# +# The top-level is a list of controllers, either for USB or PCI(e). +# Every controller needs to have a 'type' key set to either 'usb-controller' or +# 'pci-controller'. +# Every controller needs to be uniquely identified on the platform. To achieve +# this, several optional keys can be used: +# - dt-mmio: identify the MMIO address of the controller as defined in the +# Devicetree. +# - of-fullname-regex: regular expression to match against the OF_FULLNAME +# property. Useful when the controller's address is not unique across other +# sibling controllers. In this case, dt-mmio can't be used, and this property +# allows the matching to include parent nodes as well to make it unique. +# - usb-version: for USB controllers to differentiate between USB3 and USB2 +# buses sharing the same controller. +# - acpi-uid: _UID property of the controller as supplied by the ACPI. Useful to +# distinguish between multiple PCI host controllers. +# +# The 'devices' key defines a list of devices that are accessible under that +# controller. A device might be a leaf device or another controller (see +# 'Dell Inc.,XPS 13 9300.yaml'). +# +# The 'path' key is needed for every child device (that is, not top-level) to +# define how to reach this device from the parent controller. For USB devices it +# follows the format \d(.\d)* and denotes the port in the hub at each level in +# the USB topology. For PCI devices it follows the format \d.\d(/\d.\d)* +# denoting the device (identified by device-function pair) at each level in the +# PCI topology. +# +# The 'name' key is used in the leaf devices to name the device for clarity in +# the test output. +# +# For USB leaf devices, the 'interfaces' key should contain a list of the +# interfaces in that device that should be bound to a driver. +# +- type: usb-controller + dt-mmio: 11200000 + usb-version: 2 + devices: + - path: 1.4.1 + interfaces: [0, 1] + name: camera + - path: 1.4.2 + interfaces: [0, 1] + name: bluetooth +- type: pci-controller + dt-mmio: 11230000 + devices: + - path: 0.0/0.0 + name: wifi diff --git a/tools/testing/selftests/devices/probe/ksft.py b/tools/testing/selftests/devices/probe/ksft.py new file mode 100644 index 000000000000..cd89fb2bc10e --- /dev/null +++ b/tools/testing/selftests/devices/probe/ksft.py @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2023 Collabora Ltd +# +# Kselftest helpers for outputting in KTAP format. Based on kselftest.h. +# + +import sys + +ksft_cnt = {"pass": 0, "fail": 0, "skip": 0} +ksft_num_tests = 0 +ksft_test_number = 1 + +KSFT_PASS = 0 +KSFT_FAIL = 1 +KSFT_SKIP = 4 + + +def print_header(): + print("TAP version 13") + + +def set_plan(num_tests): + global ksft_num_tests + ksft_num_tests = num_tests + print("1..{}".format(num_tests)) + + +def print_cnts(): + print( + f"# Totals: pass:{ksft_cnt['pass']} fail:{ksft_cnt['fail']} xfail:0 xpass:0 skip:{ksft_cnt['skip']} error:0" + ) + + +def print_msg(msg): + print(f"# {msg}") + + +def _test_print(result, description, directive=None): + if directive: + directive_str = f"# {directive}" + else: + directive_str = "" + + global ksft_test_number + print(f"{result} {ksft_test_number} {description} {directive_str}") + ksft_test_number += 1 + + +def test_result_pass(description): + _test_print("ok", description) + ksft_cnt["pass"] += 1 + + +def test_result_fail(description): + _test_print("not ok", description) + ksft_cnt["fail"] += 1 + + +def test_result_skip(description): + _test_print("ok", description, "SKIP") + ksft_cnt["skip"] += 1 + + +def test_result(condition, description=""): + if condition: + test_result_pass(description) + else: + test_result_fail(description) + + +def finished(): + if ksft_cnt["pass"] == ksft_num_tests: + exit_code = KSFT_PASS + else: + exit_code = KSFT_FAIL + + print_cnts() + + sys.exit(exit_code) + + +def exit_fail(): + print_cnts() + sys.exit(KSFT_FAIL) + + +def exit_pass(): + print_cnts() + sys.exit(KSFT_PASS) diff --git a/tools/testing/selftests/devices/probe/test_discoverable_devices.py b/tools/testing/selftests/devices/probe/test_discoverable_devices.py new file mode 100755 index 000000000000..8f2200540a1f --- /dev/null +++ b/tools/testing/selftests/devices/probe/test_discoverable_devices.py @@ -0,0 +1,353 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2023 Collabora Ltd +# +# This script tests for presence and driver binding of devices from discoverable +# buses (ie USB, PCI). +# +# The per-platform YAML file defining the devices to be tested is stored inside +# the boards/ directory and chosen based on DT compatible or DMI IDs (sys_vendor +# and product_name). +# +# See boards/google,spherion.yaml and boards/'Dell Inc.,XPS 13 9300.yaml' for +# the description and examples of the file structure and vocabulary. +# + +import argparse +import glob +import ksft +import os +import re +import sys +import yaml + +pci_controllers = [] +usb_controllers = [] + +sysfs_usb_devices = "/sys/bus/usb/devices/" + + +def find_pci_controller_dirs(): + sysfs_devices = "/sys/devices" + pci_controller_sysfs_dir = "pci[0-9a-f]{4}:[0-9a-f]{2}" + + dir_regex = re.compile(pci_controller_sysfs_dir) + for path, dirs, _ in os.walk(sysfs_devices): + for d in dirs: + if dir_regex.match(d): + pci_controllers.append(os.path.join(path, d)) + + +def find_usb_controller_dirs(): + usb_controller_sysfs_dir = "usb[\d]+" + + dir_regex = re.compile(usb_controller_sysfs_dir) + for d in os.scandir(sysfs_usb_devices): + if dir_regex.match(d.name): + usb_controllers.append(os.path.realpath(d.path)) + + +def get_dt_mmio(sysfs_dev_dir): + re_dt_mmio = re.compile("OF_FULLNAME=.*@([0-9a-f]+)") + dt_mmio = None + + # PCI controllers' sysfs don't have an of_node, so have to read it from the + # parent + while not dt_mmio: + try: + with open(os.path.join(sysfs_dev_dir, "uevent")) as f: + dt_mmio = re_dt_mmio.search(f.read()).group(1) + return dt_mmio + except: + pass + sysfs_dev_dir = os.path.dirname(sysfs_dev_dir) + + +def get_of_fullname(sysfs_dev_dir): + re_of_fullname = re.compile("OF_FULLNAME=(.*)") + of_full_name = None + + # PCI controllers' sysfs don't have an of_node, so have to read it from the + # parent + while not of_full_name: + try: + with open(os.path.join(sysfs_dev_dir, "uevent")) as f: + of_fullname = re_of_fullname.search(f.read()).group(1) + return of_fullname + except: + pass + sysfs_dev_dir = os.path.dirname(sysfs_dev_dir) + + +def get_acpi_uid(sysfs_dev_dir): + with open(os.path.join(sysfs_dev_dir, "firmware_node", "uid")) as f: + return f.read() + + +def get_usb_version(sysfs_dev_dir): + re_usb_version = re.compile("PRODUCT=.*/(\d)/.*") + with open(os.path.join(sysfs_dev_dir, "uevent")) as f: + return int(re_usb_version.search(f.read()).group(1)) + + +def get_usb_busnum(sysfs_dev_dir): + re_busnum = re.compile("BUSNUM=(.*)") + with open(os.path.join(sysfs_dev_dir, "uevent")) as f: + return int(re_busnum.search(f.read()).group(1)) + + +def find_controller_in_sysfs(controller, parent_sysfs=None): + if controller["type"] == "pci-controller": + controllers = pci_controllers + elif controller["type"] == "usb-controller": + controllers = usb_controllers + + result_controllers = [] + + for c in controllers: + if parent_sysfs and parent_sysfs not in c: + continue + + if controller.get("dt-mmio"): + if str(controller["dt-mmio"]) != get_dt_mmio(c): + continue + + if controller.get("of-fullname-regex"): + re_of_fullname = re.compile(str(controller["of-fullname-regex"])) + if not re_of_fullname.match(get_of_fullname(c)): + continue + + if controller.get("usb-version"): + if controller["usb-version"] != get_usb_version(c): + continue + + if controller.get("acpi-uid"): + if controller["acpi-uid"] != get_acpi_uid(c): + continue + + result_controllers.append(c) + + return result_controllers + + +def is_controller(device): + return device.get("type") and "controller" in device.get("type") + + +def path_to_dir(parent_sysfs, dev_type, path): + if dev_type == "usb-device": + usb_dev_sysfs_fmt = "{}-{}" + busnum = get_usb_busnum(parent_sysfs) + dirname = os.path.join( + sysfs_usb_devices, usb_dev_sysfs_fmt.format(busnum, path) + ) + return [os.path.realpath(dirname)] + else: + pci_dev_sysfs_fmt = "????:??:{}" + path_glob = "" + for dev_func in path.split("/"): + dev_func = dev_func.zfill(4) + path_glob = os.path.join(path_glob, pci_dev_sysfs_fmt.format(dev_func)) + + dir_list = glob.glob(os.path.join(parent_sysfs, path_glob)) + + return dir_list + + +def find_in_sysfs(device, parent_sysfs=None): + if parent_sysfs and device.get("path"): + pathdirs = path_to_dir( + parent_sysfs, device["meta"]["type"], str(device["path"]) + ) + if len(pathdirs) != 1: + # Early return to report error + return pathdirs + pathdir = pathdirs[0] + sysfs_path = os.path.join(parent_sysfs, pathdir) + else: + sysfs_path = parent_sysfs + + if is_controller(device): + return find_controller_in_sysfs(device, sysfs_path) + else: + return [sysfs_path] + + +def check_driver_presence(sysfs_dir, current_node): + if current_node["meta"]["type"] == "usb-device": + usb_intf_fmt = "*-*:*.{}" + + interfaces = [] + for i in current_node["interfaces"]: + interfaces.append((i, usb_intf_fmt.format(i))) + + for intf_num, intf_dir_fmt in interfaces: + test_name = f"{current_node['meta']['pathname']}.{intf_num}.driver" + + intf_dirs = glob.glob(os.path.join(sysfs_dir, intf_dir_fmt)) + if len(intf_dirs) != 1: + ksft.test_result_fail(test_name) + continue + intf_dir = intf_dirs[0] + + driver_link = os.path.join(sysfs_dir, intf_dir, "driver") + ksft.test_result(os.path.isdir(driver_link), test_name) + else: + driver_link = os.path.join(sysfs_dir, "driver") + test_name = current_node["meta"]["pathname"] + ".driver" + ksft.test_result(os.path.isdir(driver_link), test_name) + + +def generate_pathname(device): + pathname = "" + + if device.get("path"): + pathname = str(device["path"]) + + if device.get("type"): + dev_type = device["type"] + if device.get("usb-version"): + dev_type = dev_type.replace("usb", "usb" + str(device["usb-version"])) + if device.get("acpi-uid") is not None: + dev_type = dev_type.replace("pci", "pci" + str(device["acpi-uid"])) + pathname = pathname + "/" + dev_type + + if device.get("dt-mmio"): + pathname += "@" + str(device["dt-mmio"]) + + if device.get("of-fullname-regex"): + pathname += "-" + str(device["of-fullname-regex"]) + + if device.get("name"): + pathname = pathname + "/" + device["name"] + + return pathname + + +def fill_meta_keys(child, parent=None): + child["meta"] = {} + + if parent: + child["meta"]["type"] = parent["type"].replace("controller", "device") + + pathname = generate_pathname(child) + if parent: + pathname = parent["meta"]["pathname"] + "/" + pathname + child["meta"]["pathname"] = pathname + + +def parse_device_tree_node(current_node, parent_sysfs=None): + if not parent_sysfs: + fill_meta_keys(current_node) + + sysfs_dirs = find_in_sysfs(current_node, parent_sysfs) + if len(sysfs_dirs) != 1: + if len(sysfs_dirs) == 0: + ksft.test_result_fail( + f"Couldn't find in sysfs: {current_node['meta']['pathname']}" + ) + else: + ksft.test_result_fail( + f"Found multiple sysfs entries for {current_node['meta']['pathname']}: {sysfs_dirs}" + ) + return + sysfs_dir = sysfs_dirs[0] + + if not is_controller(current_node): + ksft.test_result( + os.path.exists(sysfs_dir), current_node["meta"]["pathname"] + ".device" + ) + check_driver_presence(sysfs_dir, current_node) + else: + for child_device in current_node["devices"]: + fill_meta_keys(child_device, current_node) + parse_device_tree_node(child_device, sysfs_dir) + + +def count_tests(device_trees): + test_count = 0 + + def parse_node(device): + nonlocal test_count + if device.get("devices"): + for child in device["devices"]: + parse_node(child) + else: + if device.get("interfaces"): + test_count += len(device["interfaces"]) + else: + test_count += 1 + test_count += 1 + + for device_tree in device_trees: + parse_node(device_tree) + + return test_count + + +def get_board_filenames(): + filenames = [] + + platform_compatible_file = "/proc/device-tree/compatible" + if os.path.exists(platform_compatible_file): + with open(platform_compatible_file) as f: + for line in f: + filenames.extend(line.split("\0")) + else: + dmi_id_dir = "/sys/devices/virtual/dmi/id" + vendor_dmi_file = os.path.join(dmi_id_dir, "sys_vendor") + product_dmi_file = os.path.join(dmi_id_dir, "product_name") + + with open(vendor_dmi_file) as f: + vendor = f.read().replace("\n", "") + with open(product_dmi_file) as f: + product = f.read().replace("\n", "") + + filenames = [vendor + "," + product] + + return filenames + + +def run_test(yaml_file): + ksft.print_msg(f"Using board file: {yaml_file}") + + with open(yaml_file) as f: + device_trees = yaml.safe_load(f) + + ksft.set_plan(count_tests(device_trees)) + + for device_tree in device_trees: + parse_device_tree_node(device_tree) + + +parser = argparse.ArgumentParser() +parser.add_argument( + "--boards-dir", default="boards", help="Directory containing the board YAML files" +) +args = parser.parse_args() + +find_pci_controller_dirs() +find_usb_controller_dirs() + +ksft.print_header() + +if not os.path.exists(args.boards_dir): + ksft.print_msg(f"Boards directory '{args.boards_dir}' doesn't exist") + ksft.exit_fail() + +board_file = "" +for board_filename in get_board_filenames(): + full_board_filename = os.path.join(args.boards_dir, board_filename + ".yaml") + + if os.path.exists(full_board_filename): + board_file = full_board_filename + break + +if not board_file: + ksft.print_msg("No matching board file found") + ksft.exit_fail() + +run_test(board_file) + +ksft.finished() diff --git a/tools/testing/selftests/devices/test_discoverable_devices.py b/tools/testing/selftests/devices/test_discoverable_devices.py deleted file mode 100755 index 8f2200540a1f..000000000000 --- a/tools/testing/selftests/devices/test_discoverable_devices.py +++ /dev/null @@ -1,353 +0,0 @@ -#!/usr/bin/python3 -# SPDX-License-Identifier: GPL-2.0 -# -# Copyright (c) 2023 Collabora Ltd -# -# This script tests for presence and driver binding of devices from discoverable -# buses (ie USB, PCI). -# -# The per-platform YAML file defining the devices to be tested is stored inside -# the boards/ directory and chosen based on DT compatible or DMI IDs (sys_vendor -# and product_name). -# -# See boards/google,spherion.yaml and boards/'Dell Inc.,XPS 13 9300.yaml' for -# the description and examples of the file structure and vocabulary. -# - -import argparse -import glob -import ksft -import os -import re -import sys -import yaml - -pci_controllers = [] -usb_controllers = [] - -sysfs_usb_devices = "/sys/bus/usb/devices/" - - -def find_pci_controller_dirs(): - sysfs_devices = "/sys/devices" - pci_controller_sysfs_dir = "pci[0-9a-f]{4}:[0-9a-f]{2}" - - dir_regex = re.compile(pci_controller_sysfs_dir) - for path, dirs, _ in os.walk(sysfs_devices): - for d in dirs: - if dir_regex.match(d): - pci_controllers.append(os.path.join(path, d)) - - -def find_usb_controller_dirs(): - usb_controller_sysfs_dir = "usb[\d]+" - - dir_regex = re.compile(usb_controller_sysfs_dir) - for d in os.scandir(sysfs_usb_devices): - if dir_regex.match(d.name): - usb_controllers.append(os.path.realpath(d.path)) - - -def get_dt_mmio(sysfs_dev_dir): - re_dt_mmio = re.compile("OF_FULLNAME=.*@([0-9a-f]+)") - dt_mmio = None - - # PCI controllers' sysfs don't have an of_node, so have to read it from the - # parent - while not dt_mmio: - try: - with open(os.path.join(sysfs_dev_dir, "uevent")) as f: - dt_mmio = re_dt_mmio.search(f.read()).group(1) - return dt_mmio - except: - pass - sysfs_dev_dir = os.path.dirname(sysfs_dev_dir) - - -def get_of_fullname(sysfs_dev_dir): - re_of_fullname = re.compile("OF_FULLNAME=(.*)") - of_full_name = None - - # PCI controllers' sysfs don't have an of_node, so have to read it from the - # parent - while not of_full_name: - try: - with open(os.path.join(sysfs_dev_dir, "uevent")) as f: - of_fullname = re_of_fullname.search(f.read()).group(1) - return of_fullname - except: - pass - sysfs_dev_dir = os.path.dirname(sysfs_dev_dir) - - -def get_acpi_uid(sysfs_dev_dir): - with open(os.path.join(sysfs_dev_dir, "firmware_node", "uid")) as f: - return f.read() - - -def get_usb_version(sysfs_dev_dir): - re_usb_version = re.compile("PRODUCT=.*/(\d)/.*") - with open(os.path.join(sysfs_dev_dir, "uevent")) as f: - return int(re_usb_version.search(f.read()).group(1)) - - -def get_usb_busnum(sysfs_dev_dir): - re_busnum = re.compile("BUSNUM=(.*)") - with open(os.path.join(sysfs_dev_dir, "uevent")) as f: - return int(re_busnum.search(f.read()).group(1)) - - -def find_controller_in_sysfs(controller, parent_sysfs=None): - if controller["type"] == "pci-controller": - controllers = pci_controllers - elif controller["type"] == "usb-controller": - controllers = usb_controllers - - result_controllers = [] - - for c in controllers: - if parent_sysfs and parent_sysfs not in c: - continue - - if controller.get("dt-mmio"): - if str(controller["dt-mmio"]) != get_dt_mmio(c): - continue - - if controller.get("of-fullname-regex"): - re_of_fullname = re.compile(str(controller["of-fullname-regex"])) - if not re_of_fullname.match(get_of_fullname(c)): - continue - - if controller.get("usb-version"): - if controller["usb-version"] != get_usb_version(c): - continue - - if controller.get("acpi-uid"): - if controller["acpi-uid"] != get_acpi_uid(c): - continue - - result_controllers.append(c) - - return result_controllers - - -def is_controller(device): - return device.get("type") and "controller" in device.get("type") - - -def path_to_dir(parent_sysfs, dev_type, path): - if dev_type == "usb-device": - usb_dev_sysfs_fmt = "{}-{}" - busnum = get_usb_busnum(parent_sysfs) - dirname = os.path.join( - sysfs_usb_devices, usb_dev_sysfs_fmt.format(busnum, path) - ) - return [os.path.realpath(dirname)] - else: - pci_dev_sysfs_fmt = "????:??:{}" - path_glob = "" - for dev_func in path.split("/"): - dev_func = dev_func.zfill(4) - path_glob = os.path.join(path_glob, pci_dev_sysfs_fmt.format(dev_func)) - - dir_list = glob.glob(os.path.join(parent_sysfs, path_glob)) - - return dir_list - - -def find_in_sysfs(device, parent_sysfs=None): - if parent_sysfs and device.get("path"): - pathdirs = path_to_dir( - parent_sysfs, device["meta"]["type"], str(device["path"]) - ) - if len(pathdirs) != 1: - # Early return to report error - return pathdirs - pathdir = pathdirs[0] - sysfs_path = os.path.join(parent_sysfs, pathdir) - else: - sysfs_path = parent_sysfs - - if is_controller(device): - return find_controller_in_sysfs(device, sysfs_path) - else: - return [sysfs_path] - - -def check_driver_presence(sysfs_dir, current_node): - if current_node["meta"]["type"] == "usb-device": - usb_intf_fmt = "*-*:*.{}" - - interfaces = [] - for i in current_node["interfaces"]: - interfaces.append((i, usb_intf_fmt.format(i))) - - for intf_num, intf_dir_fmt in interfaces: - test_name = f"{current_node['meta']['pathname']}.{intf_num}.driver" - - intf_dirs = glob.glob(os.path.join(sysfs_dir, intf_dir_fmt)) - if len(intf_dirs) != 1: - ksft.test_result_fail(test_name) - continue - intf_dir = intf_dirs[0] - - driver_link = os.path.join(sysfs_dir, intf_dir, "driver") - ksft.test_result(os.path.isdir(driver_link), test_name) - else: - driver_link = os.path.join(sysfs_dir, "driver") - test_name = current_node["meta"]["pathname"] + ".driver" - ksft.test_result(os.path.isdir(driver_link), test_name) - - -def generate_pathname(device): - pathname = "" - - if device.get("path"): - pathname = str(device["path"]) - - if device.get("type"): - dev_type = device["type"] - if device.get("usb-version"): - dev_type = dev_type.replace("usb", "usb" + str(device["usb-version"])) - if device.get("acpi-uid") is not None: - dev_type = dev_type.replace("pci", "pci" + str(device["acpi-uid"])) - pathname = pathname + "/" + dev_type - - if device.get("dt-mmio"): - pathname += "@" + str(device["dt-mmio"]) - - if device.get("of-fullname-regex"): - pathname += "-" + str(device["of-fullname-regex"]) - - if device.get("name"): - pathname = pathname + "/" + device["name"] - - return pathname - - -def fill_meta_keys(child, parent=None): - child["meta"] = {} - - if parent: - child["meta"]["type"] = parent["type"].replace("controller", "device") - - pathname = generate_pathname(child) - if parent: - pathname = parent["meta"]["pathname"] + "/" + pathname - child["meta"]["pathname"] = pathname - - -def parse_device_tree_node(current_node, parent_sysfs=None): - if not parent_sysfs: - fill_meta_keys(current_node) - - sysfs_dirs = find_in_sysfs(current_node, parent_sysfs) - if len(sysfs_dirs) != 1: - if len(sysfs_dirs) == 0: - ksft.test_result_fail( - f"Couldn't find in sysfs: {current_node['meta']['pathname']}" - ) - else: - ksft.test_result_fail( - f"Found multiple sysfs entries for {current_node['meta']['pathname']}: {sysfs_dirs}" - ) - return - sysfs_dir = sysfs_dirs[0] - - if not is_controller(current_node): - ksft.test_result( - os.path.exists(sysfs_dir), current_node["meta"]["pathname"] + ".device" - ) - check_driver_presence(sysfs_dir, current_node) - else: - for child_device in current_node["devices"]: - fill_meta_keys(child_device, current_node) - parse_device_tree_node(child_device, sysfs_dir) - - -def count_tests(device_trees): - test_count = 0 - - def parse_node(device): - nonlocal test_count - if device.get("devices"): - for child in device["devices"]: - parse_node(child) - else: - if device.get("interfaces"): - test_count += len(device["interfaces"]) - else: - test_count += 1 - test_count += 1 - - for device_tree in device_trees: - parse_node(device_tree) - - return test_count - - -def get_board_filenames(): - filenames = [] - - platform_compatible_file = "/proc/device-tree/compatible" - if os.path.exists(platform_compatible_file): - with open(platform_compatible_file) as f: - for line in f: - filenames.extend(line.split("\0")) - else: - dmi_id_dir = "/sys/devices/virtual/dmi/id" - vendor_dmi_file = os.path.join(dmi_id_dir, "sys_vendor") - product_dmi_file = os.path.join(dmi_id_dir, "product_name") - - with open(vendor_dmi_file) as f: - vendor = f.read().replace("\n", "") - with open(product_dmi_file) as f: - product = f.read().replace("\n", "") - - filenames = [vendor + "," + product] - - return filenames - - -def run_test(yaml_file): - ksft.print_msg(f"Using board file: {yaml_file}") - - with open(yaml_file) as f: - device_trees = yaml.safe_load(f) - - ksft.set_plan(count_tests(device_trees)) - - for device_tree in device_trees: - parse_device_tree_node(device_tree) - - -parser = argparse.ArgumentParser() -parser.add_argument( - "--boards-dir", default="boards", help="Directory containing the board YAML files" -) -args = parser.parse_args() - -find_pci_controller_dirs() -find_usb_controller_dirs() - -ksft.print_header() - -if not os.path.exists(args.boards_dir): - ksft.print_msg(f"Boards directory '{args.boards_dir}' doesn't exist") - ksft.exit_fail() - -board_file = "" -for board_filename in get_board_filenames(): - full_board_filename = os.path.join(args.boards_dir, board_filename + ".yaml") - - if os.path.exists(full_board_filename): - board_file = full_board_filename - break - -if not board_file: - ksft.print_msg("No matching board file found") - ksft.exit_fail() - -run_test(board_file) - -ksft.finished() -- cgit v1.2.3 From 0e7b7bde460304f44e8c6b212c3195ac2f69f6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=ADcolas=20F=2E=20R=2E=20A=2E=20Prado?= Date: Fri, 5 Jul 2024 19:29:55 -0400 Subject: kselftest: Move ksft helper module to common directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the ksft python module, which provides generic helpers for kselftests, to a common directory so it can be more easily shared between different tests. Signed-off-by: Nícolas F. R. A. Prado Acked-by: Shuah Khan Acked-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240705-dev-err-log-selftest-v2-2-163b9cd7b3c1@collabora.com Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/devices/probe/Makefile | 2 +- tools/testing/selftests/devices/probe/ksft.py | 90 ---------------------- .../devices/probe/test_discoverable_devices.py | 7 +- tools/testing/selftests/kselftest/ksft.py | 90 ++++++++++++++++++++++ 5 files changed, 98 insertions(+), 92 deletions(-) delete mode 100644 tools/testing/selftests/devices/probe/ksft.py create mode 100644 tools/testing/selftests/kselftest/ksft.py (limited to 'tools/testing') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 37214201d974..7bd78b9f5cdd 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -251,6 +251,7 @@ ifdef INSTALL_PATH install -m 744 kselftest/runner.sh $(INSTALL_PATH)/kselftest/ install -m 744 kselftest/prefix.pl $(INSTALL_PATH)/kselftest/ install -m 744 kselftest/ktap_helpers.sh $(INSTALL_PATH)/kselftest/ + install -m 744 kselftest/ksft.py $(INSTALL_PATH)/kselftest/ install -m 744 run_kselftest.sh $(INSTALL_PATH)/ rm -f $(TEST_LIST) @ret=1; \ diff --git a/tools/testing/selftests/devices/probe/Makefile b/tools/testing/selftests/devices/probe/Makefile index 7a6eaa031cfe..f630108c3fdf 100644 --- a/tools/testing/selftests/devices/probe/Makefile +++ b/tools/testing/selftests/devices/probe/Makefile @@ -1,4 +1,4 @@ TEST_PROGS := test_discoverable_devices.py -TEST_FILES := boards ksft.py +TEST_FILES := boards include ../../lib.mk diff --git a/tools/testing/selftests/devices/probe/ksft.py b/tools/testing/selftests/devices/probe/ksft.py deleted file mode 100644 index cd89fb2bc10e..000000000000 --- a/tools/testing/selftests/devices/probe/ksft.py +++ /dev/null @@ -1,90 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Copyright (c) 2023 Collabora Ltd -# -# Kselftest helpers for outputting in KTAP format. Based on kselftest.h. -# - -import sys - -ksft_cnt = {"pass": 0, "fail": 0, "skip": 0} -ksft_num_tests = 0 -ksft_test_number = 1 - -KSFT_PASS = 0 -KSFT_FAIL = 1 -KSFT_SKIP = 4 - - -def print_header(): - print("TAP version 13") - - -def set_plan(num_tests): - global ksft_num_tests - ksft_num_tests = num_tests - print("1..{}".format(num_tests)) - - -def print_cnts(): - print( - f"# Totals: pass:{ksft_cnt['pass']} fail:{ksft_cnt['fail']} xfail:0 xpass:0 skip:{ksft_cnt['skip']} error:0" - ) - - -def print_msg(msg): - print(f"# {msg}") - - -def _test_print(result, description, directive=None): - if directive: - directive_str = f"# {directive}" - else: - directive_str = "" - - global ksft_test_number - print(f"{result} {ksft_test_number} {description} {directive_str}") - ksft_test_number += 1 - - -def test_result_pass(description): - _test_print("ok", description) - ksft_cnt["pass"] += 1 - - -def test_result_fail(description): - _test_print("not ok", description) - ksft_cnt["fail"] += 1 - - -def test_result_skip(description): - _test_print("ok", description, "SKIP") - ksft_cnt["skip"] += 1 - - -def test_result(condition, description=""): - if condition: - test_result_pass(description) - else: - test_result_fail(description) - - -def finished(): - if ksft_cnt["pass"] == ksft_num_tests: - exit_code = KSFT_PASS - else: - exit_code = KSFT_FAIL - - print_cnts() - - sys.exit(exit_code) - - -def exit_fail(): - print_cnts() - sys.exit(KSFT_FAIL) - - -def exit_pass(): - print_cnts() - sys.exit(KSFT_PASS) diff --git a/tools/testing/selftests/devices/probe/test_discoverable_devices.py b/tools/testing/selftests/devices/probe/test_discoverable_devices.py index 8f2200540a1f..d94a74b8a054 100755 --- a/tools/testing/selftests/devices/probe/test_discoverable_devices.py +++ b/tools/testing/selftests/devices/probe/test_discoverable_devices.py @@ -16,12 +16,17 @@ import argparse import glob -import ksft import os import re import sys import yaml +# Allow ksft module to be imported from different directory +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(this_dir, "../../kselftest/")) + +import ksft + pci_controllers = [] usb_controllers = [] diff --git a/tools/testing/selftests/kselftest/ksft.py b/tools/testing/selftests/kselftest/ksft.py new file mode 100644 index 000000000000..cd89fb2bc10e --- /dev/null +++ b/tools/testing/selftests/kselftest/ksft.py @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2023 Collabora Ltd +# +# Kselftest helpers for outputting in KTAP format. Based on kselftest.h. +# + +import sys + +ksft_cnt = {"pass": 0, "fail": 0, "skip": 0} +ksft_num_tests = 0 +ksft_test_number = 1 + +KSFT_PASS = 0 +KSFT_FAIL = 1 +KSFT_SKIP = 4 + + +def print_header(): + print("TAP version 13") + + +def set_plan(num_tests): + global ksft_num_tests + ksft_num_tests = num_tests + print("1..{}".format(num_tests)) + + +def print_cnts(): + print( + f"# Totals: pass:{ksft_cnt['pass']} fail:{ksft_cnt['fail']} xfail:0 xpass:0 skip:{ksft_cnt['skip']} error:0" + ) + + +def print_msg(msg): + print(f"# {msg}") + + +def _test_print(result, description, directive=None): + if directive: + directive_str = f"# {directive}" + else: + directive_str = "" + + global ksft_test_number + print(f"{result} {ksft_test_number} {description} {directive_str}") + ksft_test_number += 1 + + +def test_result_pass(description): + _test_print("ok", description) + ksft_cnt["pass"] += 1 + + +def test_result_fail(description): + _test_print("not ok", description) + ksft_cnt["fail"] += 1 + + +def test_result_skip(description): + _test_print("ok", description, "SKIP") + ksft_cnt["skip"] += 1 + + +def test_result(condition, description=""): + if condition: + test_result_pass(description) + else: + test_result_fail(description) + + +def finished(): + if ksft_cnt["pass"] == ksft_num_tests: + exit_code = KSFT_PASS + else: + exit_code = KSFT_FAIL + + print_cnts() + + sys.exit(exit_code) + + +def exit_fail(): + print_cnts() + sys.exit(KSFT_FAIL) + + +def exit_pass(): + print_cnts() + sys.exit(KSFT_PASS) -- cgit v1.2.3 From b727493011123db329e2901e3abf81a8d146b6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=ADcolas=20F=2E=20R=2E=20A=2E=20Prado?= Date: Fri, 5 Jul 2024 19:29:56 -0400 Subject: kselftest: devices: Add test to detect device error logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Log errors are the most widely used mechanism for reporting issues in the kernel. When an error is logged using the device helpers, eg dev_err(), it gets metadata attached that identifies the subsystem and device where the message is coming from. Introduce a new test that makes use of that metadata to report which devices logged errors (or more critical messages). Signed-off-by: Nícolas F. R. A. Prado Acked-by: Shuah Khan Acked-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240705-dev-err-log-selftest-v2-3-163b9cd7b3c1@collabora.com Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/Makefile | 1 + .../testing/selftests/devices/error_logs/Makefile | 3 + .../devices/error_logs/test_device_error_logs.py | 85 ++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 tools/testing/selftests/devices/error_logs/Makefile create mode 100755 tools/testing/selftests/devices/error_logs/test_device_error_logs.py (limited to 'tools/testing') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 7bd78b9f5cdd..c4937c87df22 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -13,6 +13,7 @@ TARGETS += core TARGETS += cpufreq TARGETS += cpu-hotplug TARGETS += damon +TARGETS += devices/error_logs TARGETS += devices/probe TARGETS += dmabuf-heaps TARGETS += drivers/dma-buf diff --git a/tools/testing/selftests/devices/error_logs/Makefile b/tools/testing/selftests/devices/error_logs/Makefile new file mode 100644 index 000000000000..d546c3fb0a7f --- /dev/null +++ b/tools/testing/selftests/devices/error_logs/Makefile @@ -0,0 +1,3 @@ +TEST_PROGS := test_device_error_logs.py + +include ../../lib.mk diff --git a/tools/testing/selftests/devices/error_logs/test_device_error_logs.py b/tools/testing/selftests/devices/error_logs/test_device_error_logs.py new file mode 100755 index 000000000000..3dd56c8ec92c --- /dev/null +++ b/tools/testing/selftests/devices/error_logs/test_device_error_logs.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2024 Collabora Ltd +# +# This test checks for the presence of error (or more critical) log messages +# coming from devices in the kernel log. +# +# One failed test case is reported for each device that has outputted error +# logs. Devices with no errors do not produce a passing test case to avoid +# polluting the results, therefore a successful run will list 0 tests run. +# + +import glob +import os +import re +import sys + +# Allow ksft module to be imported from different directory +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(this_dir, "../../kselftest/")) + +import ksft + +kmsg = "/dev/kmsg" + +RE_log = re.compile( + r"(?P[0-9]+),(?P[0-9]+),(?P[0-9]+),(?P[^;]*)(,[^;]*)*;(?P.*)" +) +RE_tag = re.compile(r" (?P[^=]+)=(?P.*)") + +PREFIX_ERROR = 3 + +logs = [] +error_log_per_device = {} + + +def parse_kmsg(): + current_log = {} + + with open(kmsg) as f: + os.set_blocking(f.fileno(), False) + + for line in f: + tag_line = RE_tag.match(line) + log_line = RE_log.match(line) + + if log_line: + if current_log: + logs.append(current_log) # Save last log + + current_log = { + "prefix": int(log_line.group("prefix")), + "sequence": int(log_line.group("sequence")), + "timestamp": int(log_line.group("timestamp")), + "flag": log_line.group("flag"), + "message": log_line.group("message"), + } + elif tag_line: + current_log[tag_line.group("key")] = tag_line.group("value") + + +def generate_per_device_error_log(): + for log in logs: + if log.get("DEVICE") and log["prefix"] <= PREFIX_ERROR: + if not error_log_per_device.get(log["DEVICE"]): + error_log_per_device[log["DEVICE"]] = [] + error_log_per_device[log["DEVICE"]].append(log) + + +parse_kmsg() + +generate_per_device_error_log() +num_tests = len(error_log_per_device) + +ksft.print_header() +ksft.set_plan(num_tests) + +for device in error_log_per_device: + for log in error_log_per_device[device]: + ksft.print_msg(log["message"]) + ksft.test_result_fail(device) +if num_tests == 0: + ksft.print_msg("No device error logs found") +ksft.finished() -- cgit v1.2.3