
Linux 内核 kselftest 设备测试指南dt、error_logs、probe 与 exist 四类测试全景解析【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux本指南基于内核源码树中的 Documentation/dev-tools/testing-devices.rst系统梳理 kselftest 框架下用于通用设备测试的几类测试Devicetree 探测状态测试dt、设备错误日志测试devices/error_logs、可发现总线设备探测测试devices/probe以及设备存在性测试devices/exist。读完本文你将掌握每一类测试的覆盖范围、前置需求、底层实现原理以及如何为你的平台挑选并启用合适的设备测试从而在 CI 中持续发现设备探测失败、驱动未绑定等回归问题。为什么需要面向设备的 kselftest内核开发者最担心的回归之一是某个驱动改动导致设备在特定平台上无法探测、无法绑定驱动。这类问题往往依赖真实硬件才能复现普通单元测试难以覆盖。kselftest 提供了一批面向设备的测试其目标是在尽可能多的真实平台上自动运行通过检查 sysfs、devicetree 与内核日志来验证设备是否出现、是否被驱动接管、是否输出错误日志。这类测试之间存在一定的覆盖重叠且各自有不同的运行需求。本文按文档给出的四个测试逐一展开并从源码层面说明其实现机制。说明原文档中的路径均相对于 kselftest 目录tools/testing/selftests书写本文所有引用已转换为以仓库根目录为起点的相对路径。四类设备测试总览原文档给出了一览表这是理解整套测试布局的骨架整理如下测试目录相对仓库根覆盖范围前置需求Devicetreetools/testing/selftests/dtDevicetree 中描述的设备是否被成功探测无Error logstools/testing/selftests/devices/error_logs任意设备是否产生错误或更严重级别日志消息无Discoverable bustools/testing/selftests/devices/probe参考文件中描述的 USB / PCI 设备是否存在并被探测需在 YAML 参考文件中手工描述待测设备Exist文档指向devices/exist详见下文说明所有设备的存在性需在 known-good 内核上生成参考数据从部署成本看原文档给出的建议非常明确由于dt与error_logs没有任何前置需求应在所有基于 Devicetree 的平台上启用它们随后为每个平台生成参考数据并启用exist测试以大幅提升覆盖率而probe可发现总线测试用于验证特定 USB/PCI 设备的探测状态对大多数场景而言可能不值得投入维护成本。dt 测试Devicetree 设备探测状态检查覆盖范围与实现原理dt测试用于发现在 Devicetree 中声明、期望绑定驱动、但实际上没有绑定的设备。其实现位于 tools/testing/selftests/dt/test_unprobed_devices.sh脚本头注释说明其基于 Frank Rowand 的dt_stat脚本改造而来。脚本的核心思路是构建两份节点集合做差集nodes_compatible遍历/proc/device-tree/下所有目录收集带有compatible属性的节点若节点存在status属性且值既不是okay也不是ok则该节点连同其所有后代被排除——这与内核在探测时跳过 disabled 节点的语义一致。nodes_dev_bound遍历/sys/devices下所有目录仅收集同时具备uevent文件且存在driver子目录的设备再从uevent中提取OF_FULLNAME值得到已绑定驱动的节点全名列表。随后对每个nodes_compatible中的节点检查其是否出现在已绑定列表中。若未绑定则读取该节点的所有 compatible 字符串并逐一判断若 compatible 出现在compatible_ignore_list中则跳过忽略若 compatible 出现在compatible_list中报告FAIL说明这是一个应有驱动却未探测的设备否则报告SKIP该 compatible 没有任何已知驱动视为正常。已绑定的节点则报告PASS。脚本最终以 KTAP 格式输出结果并返回整体退出码。两个列表的来源compatible_list并非手工维护而是由构建系统生成。看 tools/testing/selftests/dt/Makefile它调用 scripts/dtc/dt-extract-compatibles 对整个内核源码树做静态扫描$(OUTPUT)/compatible_list: $(top_srcdir)/scripts/dtc/dt-extract-compatibles -d $(top_srcdir) $从该 Python 脚本源码可以看出它通过正则解析三类来源提取 compatible 字符串OF_DECLARE()/OF_MATCH()/IRQCHIP_DECLARE()等宏排除CPU_METHOD_OF_DECLARE、of_device_id结构体数组中的.compatible字段、以及of_device_is_compatible()等函数调用。这意味着哪些 compatible 应有驱动这一知识直接来自内核源码本身测试运行时不需要额外输入。compatible_ignore_listtools/testing/selftests/dt/compatible_ignore_list 当前仅包含一行simple-mfd。simple-mfd属于纯容器节点类型其下子节点各自有驱动父节点本身通常不会被绑定因此被显式排除在应绑定集合之外。另外注意 Makefile 的逻辑若系统缺少 python3该测试会打印 Missing python3. This test will be skipped. 并整体跳过说明生成compatible_list强依赖 python3 环境。error_logs 测试设备错误日志检查覆盖范围与运行语义devices/error_logs测试检查内核日志中是否出现来自任意设备的错误或更严重级别日志消息。其实现为 tools/testing/selftests/devices/error_logs/test_device_error_logs.py脚本头注释明确了其独特的通过/失败语义每个输出过错误日志的设备会报告一个失败用例没有错误的设备不会产生通过用例以免污染结果。因此一次完全成功的运行会显示 0 tests run。也就是说这是一个反证式测试ksft.set_plan()的测试计划数量等于存在错误日志的设备数而每个计划用例都调用ksft.test_result_fail(device)标记失败。当所有设备都健康时计划数为 0输出No device error logs found。实现机制解析 /dev/kmsg脚本的核心是解析/dev/kmsg非阻塞方式读取os.set_blocking(f.fileno(), False)其日志行格式与内核 printk 记录对应RE_log re.compile( r(?Pprefix[0-9]),(?Psequence[0-9]),(?Ptimestamp[0-9]),(?Pflag[^;]*)(,[^;]*)*;(?Pmessage.*) ) RE_tag re.compile(r (?Pkey[^])(?Pvalue.*))其中prefix即日志级别0~7数字越小越严重脚本定义PREFIX_ERROR 3即只关注级别 3KERN_ERR及更严重的记录。对于每条错误日志若其附带结构化字段DEVICExxx即日志来自某个已注册的设备携带dev_name()信息则按设备名归类到error_log_per_device。最终每个出现错误日志的设备被报告为一个失败用例并打印该设备对应的原始错误消息方便定位。probe 测试可发现总线设备探测检查测试目标与 YAML 参考文件devices/probe用于检查 USB 与 PCI 总线上的设备是否存在、是否绑定了驱动。其实现为 tools/testing/selftests/devices/probe/test_discoverable_devices.py。与前面两类零配置测试不同它要求为每个被测平台手工维护一份 YAML 参考文件描述该平台应当出现哪些设备。YAML 文件存放在boards/目录文件名即平台标识基于 Devicetree 的平台文件名取自/proc/device-tree/compatible中的某个 compatible 字符串例如 Google Spherion Chromebook 对应 tools/testing/selftests/devices/probe/boards/google,spherion.yaml基于 ACPI / x86 的平台文件名采用Vendor,Product格式取自/sys/devices/virtual/dmi/id/sys_vendor与/sys/devices/virtual/dmi/id/product_name例如 tools/testing/selftests/devices/probe/boards/Dell Inc.,XPS 13 9300.yaml。运行时的板卡匹配逻辑见get_board_filenames()优先读取/proc/device-tree/compatible按\0分隔枚举所有 compatible逐个尝试拼接为compatible.yaml不存在该文件时退回 DMI 的 vendor/product 组合。可通过--boards-dir参数指定板卡目录默认boards。若找不到匹配的板卡文件测试直接以失败退出。YAML 文件结构与关键字段以google,spherion.yaml为例完整的设备定义如下该文件头部的注释本身就是字段字典- 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字段语义总结type必填控制器级为usb-controller或pci-controller叶子设备同样可用type声明此时父控制器类型中的 controller 被替换为 device见fill_meta_keys()。控制器唯一标识选填至少满足其一以便在平台上唯一定位dt-mmio控制器在 Devicetree 中定义的 MMIO 地址十六进制字符串of-fullname-regex与OF_FULLNAME属性匹配的正则表达式当控制器地址在兄弟节点间不唯一时使用此时不能用dt-mmio可结合父节点路径使匹配唯一usb-version用于区分共享同一控制器的 USB3 与 USB2 总线值为 2 或 3acpi-uid控制器在 ACPI 中提供的_UID属性用于区分多个 PCI host controller。devices该控制器下可访问的设备列表设备可以是叶子设备也可以是另一个控制器嵌套结构见 XPS 13 9300 示例。path每个非顶层设备必填从父控制器到达该设备的路径。USB 设备格式为\d(.\d)*表示 USB 拓扑中每一级 hub 的端口号PCI 设备格式为\d.\d(/\d.\d)*表示每一级 PCI 拓扑中的 device-function 对。name叶子设备的可读名称用于测试输出。interfacesUSB 叶子设备专用列出该设备中应绑定驱动的接口号列表。再看 x86 平台的嵌套示例XPS 13 9300单 PCI host controller因此未提供任何控制器标识字段——注释指出若存在多个控制器可用acpi-uid区分- type: pci-controller # 该机器只有一个 PCI host controller因此可以不提供标识字段 #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底层查找逻辑控制器发现与设备匹配脚本启动时预先扫描两处 sysfsPCI 控制器find_pci_controller_dirs()递归遍历/sys/devices用正则pci[0-9a-f]{4}:[0-9a-f]{2}匹配控制器目录USB 控制器find_usb_controller_dirs()遍历/sys/bus/usb/devices/用正则usb[\d]匹配。在find_controller_in_sysfs()中依据 YAML 中的dt-mmio、of-fullname-regex、usb-version、acpi-uid等键对候选控制器过滤其中dt-mmio与OF_FULLNAME均通过读取控制器或其父目录的uevent文件获得——脚本注释特别说明PCI 控制器的 sysfs 目录没有of_node因此需要沿父目录向上读取OF_FULLNAME...addr来提取 MMIO 地址。叶子设备则依据path定位USB 设备按BUSNUM-path格式在/sys/bus/usb/devices/下定位busnum从父控制器uevent的BUSNUM解析PCI 设备按????:??:devfn模式逐级 glob 匹配devfn由 4 位十六进制补齐例如path: 1d.0/0.0对应1d.0桥下的00.0功能。对每个叶子设备脚本生成pathname.device用例检查设备是否存在于 sysfs再调用check_driver_presence()普通设备检查其 sysfs 目录下是否存在driver符号链接USB 设备则对 YAML 中列出的每个接口号检查*-*:*.N接口目录下的driver链接测试名形如pathname.intf.driver。若设备在 sysfs 中找不到或找到多个匹配项均报告失败。exist 测试全设备存在性检查原文档描述的第四类测试是devices/exist覆盖范围所有设备的存在性需求在 known-good 内核上生成参考数据文档指向devices/exist/README.rst了解细节。其思路是先在已知正常的内核上采集一次设备清单作为参考基线之后在每次测试运行中对比当前设备集合与基线从而捕获某个设备消失了这类回归。由于它不依赖 Devicetree 或具体总线理论上可以覆盖probe与dt之外的设备。需要如实说明的是在本仓库当前源码树中tools/testing/selftests/devices/下仅存在error_logs与probe两个子目录见 tools/testing/selftests/Makefile 第 19~20 行注册的devices/error_logs与devices/probe两个 targetexist测试尚未合入本树上述信息来自 testing-devices.rst 本身的描述。使用该测试时请以所部署内核版本中实际提供的devices/exist/README.rst为准。如何构建与运行设备测试这三类已合入的测试均通过 kselftest 框架的lib.mk集成devices/probe/Makefile声明TEST_PROGS : test_discoverable_devices.py与TEST_FILES : boards板卡 YAML 随测试一起安装devices/error_logs/Makefile声明TEST_PROGS : test_device_error_logs.pydt/Makefile则声明生成文件compatible_list与随附文件compatible_ignore_list。因此可以按 kselftest 标准流程构建并运行这些目标顶层TARGETS已在 tools/testing/selftests/Makefile 中注册# 构建并运行 dt 与 devices 系列测试 make -C tools/testing/selftests TARGETSdt devices/error_logs devices/probe run_tests # 或先构建安装再单独执行 make -C tools/testing/selftests TARGETSdt devices/error_logs devices/probe install ./kselftest/run_kselftest.sh -t dt -t devices/error_logs -t devices/probe运行环境要求必须运行在真实硬件上/proc/device-tree、/sys/devices、/dev/kmsg均为运行期接口且需要 root 权限读取内核日志dt测试依赖 python3缺少时整体跳过compatible_list在构建期由 scripts/dtc/dt-extract-compatibles 从源码生成devices/probe测试依赖 python3 的 yaml 模块并且只有当boards/目录中存在匹配当前平台的 YAML 文件时才会真正执行。部署建议与取舍结合原文档的结论与上述源码分析可以给出如下落地策略所有基于 Devicetree 的平台无条件启用dt与devices/error_logs。二者零配置、零维护成本dt能在驱动删除或 compatible 匹配回归时第一时间报警error_logs则持续监督设备侧的异常日志输出。追求高覆盖率在每个受支持的平台上于 known-good 内核上生成exist测试的参考数据并启用它用最小的配置成本获得所有设备存在性的广覆盖。特定设备守护若某平台对特定 USB/PCI 外设如摄像头、蓝牙、WiFi、SSD的探测状态极其敏感可为该平台维护一份probe的 YAML 板卡文件逐接口校验驱动绑定。考虑到 YAML 需要随硬件拓扑变动而维护原文档的判断是对大多数场景可能不值得。四类测试覆盖维度互补dt管 Devicetree 设备该绑定而没绑定exist管一切设备该出现而没出现error_logs管设备运行是否报错probe管特定可发现总线设备精确存在且驱动就位。按上述梯度部署即可在不引入大量手工配置的前提下构建一套覆盖内核设备探测链路的回归防线。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考