编写设备驱动程序

驱动程序强化测试工具

驱动程序强化测试工具测试是否已正确实现 I/O 故障服务和防御性编程要求。强化的设备驱动程序可从潜在的硬件故障中复原。必须在驱动程序开发过程中测试设备驱动程序的复原能力。此类测试要求驱动程序以受控制并且可重复的方式处理多种典型硬件故障。通过驱动程序测试工具可在软件中仿真此类硬件故障。

驱动程序强化测试工具是一种 Solaris 设备驱动程序开发工具。该测试工具可在处于开发阶段的驱动程序访问其硬件时注入多种仿真硬件故障。本节介绍如何配置测试工具、创建错误注入规范(称为 errdef)以及对设备驱动程序执行测试。

测试工具可截获从驱动程序到各种 DDI 例程的调用,然后损坏调用的结果,就像硬件引起损坏一样。此外,该工具还允许损坏对特定寄存器的访问以及定义更多随机类型的损坏。

测试工具可以在运行指定的工作负荷期间通过跟踪所有寄存器访问以及直接内存访问 (direct memory access, DMA) 和中断使用情况自动生成测试脚本。生成的脚本将在向每个访问中注入一组故障的同时,重新运行该工作负荷。

驱动程序测试器应从生成的脚本中删除重复的测试案例。

测试工具作为名为 bofi(表示 bus_ops 故障注入)的设备驱动程序和两个用户级实用程序(th_define(1M)th_manage(1M))来实现。

测试工具可执行以下任务:

故障注入

驱动程序强化测试工具截获并在请求时损坏驱动程序对其硬件进行的每次访问。本节提供您在创建故障以测试驱动程序的可复原性时应了解的信息。

Solaris 设备在名为设备树(devinfo 树)的类似树的结构内进行管理。devinfo 树的每个节点都存储着与系统中某一设备的特定实例相关的信息。每个叶节点对应于一个设备驱动程序,而其他所有节点都称为子树节点。通常,结点 (nexus) 表示总线。总线节点将叶驱动程序与总线相关项隔离,从而可以生成在体系结构上独立的驱动程序。

许多 DDI 函数(特别是数据访问函数)都会导致向上调用总线结点驱动程序。当叶驱动程序访问其硬件时,它会将句柄传递给访问例程。总线结点了解如何处理句柄和实现请求。符合 DDI 标准的驱动程序只通过使用这些 DDI 访问例程来访问硬件。在这些向上调用到达指定的总线结点之前,测试工具会截获这些向上调用。如果数据访问与驱动程序测试器指定的标准相符,访问将被损坏。如果数据访问与该标准不符,则将其提供给总线结点,以便通过常规方式进行处理。

驱动程序通过使用 ddi_regs_map_setup(9F) 函数获取访问句柄:

ddi_regs_map_setup(dip, rset, ma, offset, size, handle)

参数指定要映射哪个“板外”内存。驱动程序在引用映射的 I/O 地址时必须使用返回的句柄,因为句柄用于将驱动程序与总线层次结构的详细信息隔离开来。因此,请不要直接使用返回的映射地址 ma。直接使用映射地址会导致当前以及将来无法使用数据访问函数机制。

对于程控 I/O,数据访问函数组为:

Xrepcnt 是要传送的字节数。X 是总线传送大小,为 8、16、32 或 64 字节。

DMA 具有与之类似但更为丰富的一组数据访问函数。

设置测试工具

驱动程序强化测试工具属于 Solaris Developer Cluster 的一部分。如果尚未安装此 Solaris 簇,则必须手动安装适用于您平台的测试工具软件包。

安装测试工具

要安装测试工具软件包(SUNWftduu 和 SUNWftdur),请使用 pkgadd(1M) 命令。

以超级用户身份转到软件包所在目录,并键入:


# pkgadd -d . SUNWftduu SUNWftdur

配置测试工具

安装测试工具后,请在 /kernel/drv/bofi.conf 文件中设置属性,以将工具配置为与驱动程序交互。完成工具配置后,重新引导系统以装入工具驱动程序。

测试工具行为由 /kernel/drv/bofi.conf 配置文件中设置的引导时属性控制。

首次安装工具时,可通过设置以下属性来使工具可以截获对驱动程序的 DDI 访问:

bofi-nexus

总线结点类型,如 PCI 总线

bofi-to-test

所测试的驱动程序的名称

例如,要测试名为 xyznetdrv 的 PCI 总线网络驱动程序,请设置以下属性值:

bofi-nexus="pci"
bofi-to-test="xyznetdrv"

其他属性与用于从使用 PIO 的外围设备读取和写入以及与使用 DMA 的外围设备进行双向数据传送的 Solaris DDI 数据访问机制的使用和工具检查相关。

bofi-range-check

设置此属性时,测试工具将检查传递给 PIO 数据访问函数的参数的一致性。

bofi-ddi-check

设置此属性时,测试工具将验证 ddi_map_regs_setup(9F) 返回的映射地址未在数据访问函数的上下文之外使用。

bofi-sync-check

设置此属性时,测试工具将验证 DMA 函数的使用是否正确并确保驱动程序对 ddi_dma_sync(9F) 的使用符合规范。

测试驱动程序

本节介绍如何使用 th_define(1M)th_manage(1M) 命令创建并注入故障。

创建故障

th_define 实用程序为 bofi 设备驱动程序提供了一个接口,以用于定义 errdef。errdef 对应于有关如何损坏设备驱动程序对其硬件的访问的规范。th_define 命令行参数确定要注入的故障的确切性质。如果提供的参数定义了一致的 errdef,th_define 进程将使用 bofi 驱动程序存储 errdef。该进程将使自身暂停,直至 errdef 给定的条件得到满足为止。实际上,当访问计数达到零 (0) 时,暂停将结束。

注入故障

测试工具在数据访问级别运行。数据访问具有以下特征:

测试工具截获数据访问并将适当的故障注入驱动程序。th_define(1M) 命令指定的 errdef 可对以下信息编码:

使用 -a acc_chk 选项可仿真 errdef 中的框架故障。

故障注入过程

    注入故障的过程涉及两个阶段:

  1. 使用 th_define(1M) 命令创建 errdef。

    通过向 bofi 驱动程序传递测试定义来创建 errdef,该驱动程序会存储这些定义,因此可以使用 th_manage(1M) 命令来访问它们。

  2. 创建工作负荷,然后使用 th_manage 命令激活和管理 errdef。

    th_manage 命令是到 bofi 工具驱动程序可以识别的各种 ioctl 的用户接口。th_manage 命令在驱动程序名称和实例级别运行并且包含以下命令: get_handles 用于列出访问句柄,start 用于激活 errdef,stop 用于取消激活 errdef。

    激活 errdef 将导致合格的数据访问出现故障。th_manage 实用程序支持以下命令: broadcast 用于提供 errdef 的当前状态,clear_errors 用于清除 errdef。

    有关更多信息,请参见 th_define(1M) 和 th_manage(1M) 手册页。

测试工具警告

可对测试工具进行配置,以便通过以下方法来处理警告消息:

使用第二种方法有助于确定问题的根本原因。

如果将 bofi-range-check 属性值设置为 warn,当工具检测到驱动程序违反 DDI 函数的范围时,该工具将列显以下消息(或者,如果设置为 panic,则会进入紧急状态):

ddi_getX() out of range addr %x not in %x
ddi_putX() out of range addr %x not in %x
ddi_rep_getX() out of range addr %x not in %x
ddi_rep_putX() out of range addr %x not in %x

X 为 8、16、32 或 64。

当工具已请求插入 1000 个以上额外中断时,如果驱动程序未检测到中断逾限 (jabber),则会列显以下消息:

undetected interrupt jabber - %s %d

使用脚本自动完成测试过程

可以使用日志记录访问类型 th_define(1M) 实用程序来创建故障注入测试脚本:


# th_define -n name -i instance -a log [-e fixup_script]

th_define 命令使实例脱机,然后再使其恢复联机。然后,th_define 运行 fixup_script 描述的工作负荷并记录驱动程序实例进行的 I/O 访问。

将会使用可选参数的集合调用 fixup_script 两次。会在实例脱机前调用该脚本一次,然后在实例恢复联机后再次调用该脚本。

以下变量将被传递到调用的可执行文件的环境中:

DRIVER_PATH

实例的设备路径

DRIVER_INSTANCE

驱动程序的实例编号

DRIVER_UNCONFIGURE

当实例即将脱机时设置为 1

DRIVER_CONFIGURE

当实例已恢复联机时设置为 1

通常,fixup_script 可确保所测试的设备处于适合脱机的状态(未配置)或处于适合注入错误的状态(例如,已配置、无错误并为工作负荷服务)。以下脚本是用于网络驱动程序的最小脚本:

#!/bin/ksh
driver=xyznetdrv
ifnum=$driver$DRIVER_INSTANCE
 
if [[ $DRIVER_CONFIGURE = 1 ]]; then
   ifconfig $ifnum plumb	
   ifconfig $ifnum ...	
   ifworkload start $ifnum
elif [[ $DRIVER_UNCONFIGURE = 1 ]]; then	
   ifworkload stop $ifnum	
   ifconfig $ifnum down	
   ifconfig $ifnum unplumb
fi
exit $?

注 –

ifworkload 命令应将工作负荷作为一项后台任务来启动。故障注入发生在 fixup_script 配置所测试的驱动程序并使其联机(DRIVER_CONFIGURE 设置为 1)之后。


如果存在 -e fixup_script 选项,它必须是命令行中的最后一个选项。如果不存在 -e 选项,则使用缺省脚本。缺省脚本会反复尝试使所测试的设备脱机和联机。因此,工作负荷由驱动程序的 attach()detach() 路径构成。

生成的日志将转换为一组适合运行独立的 (unassisted) 故障注入测试的可执行脚本。这些脚本创建在当前目录的子目录中,名称为 driver.test.id。脚本将在运行 fixup_script 描述的工作负荷的同时向驱动程序中注入故障,一次一个。

驱动程序测试器可对测试自动化过程生成的 errdef 进行实质性控制。请参见 th_define(1M) 手册页。

如果测试器为测试脚本选择了合适的工作负荷范围,则工具可为驱动程序各方面的强化提供良好的覆盖率。但是,要取得满覆盖率,测试器可能需要手动创建其他测试案例。请将这些案例添加至测试脚本。为确保测试可及时完成,您可能需要手动删除重复的测试案例。

自动化测试过程

    以下过程介绍了自动化测试:

  1. 确定要测试的驱动程序的各个方面。

    测试驱动程序中与硬件交互的所有方面:

    • 连接和分离

    • 在堆栈下检测和取消检测

    • 正常数据传送

    • 记录的调试模式

    必须为每种使用模式生成单独的工作负荷脚本 (fixup_script)。

  2. 对于每种使用模式,准备可执行程序 ( fixup_script),该可执行程序可对设备进行配置和取消配置,并可创建和终止工作负荷。

  3. 使用 errdef 以及访问类型 -a log 运行 th_define(1M) 命令。

  4. 等待日志填充。

    日志中包含 bofi 驱动程序的内部缓冲区的转储。此数据包含在脚本的前面。

    由于创建日志可能需要几秒钟到几分钟的时间,因此可使用 th_manage broadcast 命令检查进度。

  5. 转到已创建的测试目录并运行主测试脚本。

    主脚本将按顺序运行每个生成的测试脚本。每个寄存器集会生成单独的测试脚本。

  6. 存储结果,以用于分析。

    成功的测试结果(如 success (corruption reported)success (corruption undetected))表明所测试的驱动程序工作正常。如果工具检测到驱动程序在报告故障后无法报告服务影响或者驱动程序无法检测到访问或 DMA 句柄已被标记为有故障,则结果将报告为 failure (no service impact reported)

    输出中出现几个 test not triggered 故障并不碍事。但是,若干个此类故障将表明测试没有正常工作。当驱动程序访问的寄存器与生成测试脚本时的寄存器不同时,会出现这些故障。

  7. 同时对驱动程序的多个实例运行测试,以测试错误路径的多线程。

    例如,每个 th_define 命令都会创建一个单独的目录,其中包含测试脚本和主脚本:


    # th_define -n xyznetdrv -i 0 -a log -e script
    # th_define -n xyznetdrv -i 1 -a log -e script
    

    创建后,并行运行主脚本。


    注 –

    生成的脚本只生成仿真的故障注入,这些故障注入基于日志记录 errdef 处于活动状态期间记录的内容。定义工作负荷时,请确保记录所需结果。此外,还要分析生成的日志和故障注入规范。请验证生成的测试脚本所创建的硬件访问覆盖率是否满足需要。