恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Linux 内核 dmatest 实战指南:DMA 引擎测试客户端的构建、参数配置与实现剖析
首页
资讯中心
/
Linux 内核 dmatest 实战指南:DMA 引擎测试客户端的构建、参数配置与实现剖析
Linux 内核 dmatest 实战指南:DMA 引擎测试客户端的构建、参数配置与实现剖析
发布时间:2026/9/17 20:40:21
Linux 内核 dmatest 实战指南DMA 引擎测试客户端的构建、参数配置与实现剖析【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux本篇基于内核文档 dmatest.rst 系统讲解如何用内核自带的 dmatest 测试客户端验证 DMA 驱动涵盖CONFIG_DMATEST的构建方式、run/channel/timeout/wait等全部 sysfs 参数的操作方式、结果日志的解读并结合 dmatest.c 源码剖析测试数据的位模式设计、数据校验逻辑与通道生命周期管理帮助你在调试 DMA 驱动时快速定位拷贝、memset、XOR、RAID6 PQ 各类操作的正确性问题。dmatest 是什么能测什么、不能测什么dmatest 是一个内核内建的 DMA 测试客户端模块用于测试 DMA 引擎的memcpy、memset、XOR 和 RAID6 PQ 四种操作。它对不同长度、源/目标缓冲区中不同偏移的传输组合逐一发起 DMA 请求并用一种可复现的位模式repeatable pattern初始化源和目标缓冲区然后在传输完成后验证DMA 引擎只拷贝了被请求的区域没有多拷字节没有被错位交换swapped around源缓冲区本身没有被修改。它支持指定某个具体通道进行测试也可以同时测试多个通道还能为同一通道启动多个相互竞争的线程由threads_per_chan控制。需要注意的是两条硬约束源自原文档测试套件只对具备以下至少一项能力的通道生效DMA_MEMCPYmemory-to-memory、DMA_MEMSETconst-to-memory仿真时为 memory-to-memory、DMA_XOR、DMA_PQ。这一点在源码 dmatest.c 的dmatest_add_channel()中得到印证函数依次检查DMA_MEMCPY、DMA_MEMSET、DMA_XOR、DMA_PQ四种能力位并按能力创建对应测试线程。关于该测试框架的问题内核官方的讨论渠道是邮件列表 dmaenginevger.kernel.org。构建测试模块对应文档 Part 1在 menuconfig 中的路径为Device Drivers - DMA Engine support - DMA Test client对应配置项是CONFIG_DMATEST。从 Kconfig 可以看到其定义config DMATEST tristate DMA Test client depends on DMA_ENGINE select DMA_ENGINE_RAID help Simple DMA test client. Say N unless youre debugging a DMA Device driver.两个值得注意的细节tristate意味着它可以编译为模块M或编入内核Y两者用法不同下文分别说明它select DMA_ENGINE_RAID因为 RAID6 PQ 测试DMA_PQ依赖 RAID 辅助层编译 dmatest 会自动打开该选项。Kconfig 的帮助文本也明确定位了它的用途“除非你在调试 DMA 设备驱动否则选 N”——这是一个调试/验收工具不建议在普通生产配置中默认开启。dmatest 作为模块使用对应文档 Part 2单通道测试的三种启动方式方式一modprobe直接带参加载% modprobe dmatest timeout2000 iterations1 channeldma0chan0 run1方式二先加载再写 sysfs 参数注意必须在设置 channel 之后再写run1% modprobe dmatest % echo 2000 /sys/module/dmatest/parameters/timeout % echo 1 /sys/module/dmatest/parameters/iterations % echo dma0chan0 /sys/module/dmatest/parameters/channel % echo 1 /sys/module/dmatest/parameters/run方式三内核命令行传参等价于模块参数dmatest.timeout2000 dmatest.iterations1 dmatest.channeldma0chan0 dmatest.run1全部模块参数速查表基于源码原文档只展示了timeout、iterations、channel、run、wait几个参数dmatest.c 中实际定义的参数远比这些多完整清单如下参数类型默认值说明nobouncebool关禁用 swiotlb 弹跳缓冲改用GFP_DMA分配测试缓冲区test_buf_sizeuint16384memcpy 测试缓冲区大小字节devicestring空任意指定要测试的 DMA 引擎总线 IDthreads_per_chanuint1每个通道启动的线程数多线竞争同一通道max_channelsuint0全部允许使用的最大通道数iterationsuint0无限停止前的迭代次数dmatestuint00 测 memcpy1 测 memsetxor_sourcesuint3XOR 源缓冲区数量pq_sourcesuint3PQ 源缓冲区数量timeoutint3000传输超时毫秒传 -1 表示无限等待noverifybool关禁用数据校验norandombool关禁用随机偏移固定使用整缓冲区、零偏移verbosebool关在 dmesg 中打印每次“测试通过”的成功消息alignmentint-1自定义对齐取值为 2^alignment-1 表示使用设备声明的对齐transfer_sizeuint0自定义固定传输长度字节0 表示随机polledbool关用轮询dma_sync_wait代替中断完成通知runboolfalse启动/停止测试channelstring空任意要测试的通道名可多次写入追加多通道waitbool(只读)-读取时阻塞直到当前测试完成test_liststring(只读)-打印当前待执行pending的测试列表其中dmatest参数值得单独说明从 dmatest.c 的dmatest_add_channel()可见当通道同时支持DMA_MEMCPY和DMA_MEMSET时该参数决定创建 copy 线程还是 set 线程0 为 copy1 为 set而DMA_XOR和DMA_PQ能力不受该参数控制始终会测试。多通道测试5.0 内核起% modprobe dmatest % echo 2000 /sys/module/dmatest/parameters/timeout % echo 1 /sys/module/dmatest/parameters/iterations % echo dma0chan0 /sys/module/dmatest/parameters/channel % echo dma0chan1 /sys/module/dmatest/parameters/channel % echo dma0chan2 /sys/module/dmatest/parameters/channel % echo 1 /sys/module/dmatest/parameters/run关键规则原文档 note务必遵守从 5.0 内核开始无论是单通道还是多通道测试channel参数必须在所有其他参数设置完成之后再写入。因为正是在写入 channel 的时刻当前已设置的参数值才被“抓拍”给该通道的线程使用对应源码 add_threaded_test()它把各全局参数拷贝进struct dmatest_params后再调用request_channels()请求通道。其他参数是共享的如果在追加 channel 之前修改了其他参数那么之后所有线程都将使用新值。通道指定完成后每个线程都处于 pending 状态直到run1才统一开始执行。查询可用通道列表% ls -1 /sys/class/dma/run 参数状态查询与停止启动后内核会打印形如dmatest: Added 1 threads using dma0chan0的消息表示该通道的线程已创建并处于 pending读取cat /sys/module/dmatest/parameters/run可返回测试状态正在跑返回真值注意启动新测试不会停止正在进行的旧测试写入run0会停止线程并释放通道见下文“释放通道”一节。从源码看run参数注册了自定义的kernel_param_opsdmatest_run_set()/dmatest_run_get()写 1 且没有 pending 线程时会按“任意可用通道”策略新建测试打印No channels configured, continue with any写 0 则调用stop_threaded_test()逐个kthread_stop并释放通道。读操作则是动态的测试在跑时显示 1跑完自动回落为 0。等待测试完成wait 参数等待测试结束有两种方式用户态轮询run直到它为假使用wait参数。加载模块时指定wait1会使模块初始化过程挂起直到一轮测试跑完运行中读取/sys/module/dmatest/parameters/wait则等待当前运行中的测试结束后才返回。注意如果iterations为无限默认则等待功能被禁用——从 dmatest_wait_get() 可见其内部仅在params-iterations非零时才wait_event()。文档给出的两个完整示例% modprobe dmatest run1 iterations42 wait1 % modprobe -r dmatest% modprobe dmatest run1 iterations42 % cat /sys/module/dmatest/parameters/wait % modprobe -r dmatest随时核对运行时参数无论模块还是内建方式都可用这条命令查看当前生效的所有参数值% grep -H . /sys/module/dmatest/parameters/*编入内核时的行为差异对应文档 Part 3当CONFIG_DMATESTy时内核命令行提供的模块参数只用于第一次自动执行的测试用户态取得控制权后可用相同或不同的参数重新发起测试操作方式同上文 Part 2 模块一节。从源码可以补充一个细节dmatest_init()被注册为 late_initcall注释明确写道“when compiled-in wait for drivers to load first”——内建场景下用晚于大多数驱动的初始化级别确保执行首轮测试时各 DMA 控制器驱动已经注册好通道。内建场景下run/channel等参数同样通过 sysfs 暴露因此grep查看参数、echo修改参数的手段完全一致。收集测试结果对应文档 Part 4测试结果打印到内核日志缓冲区统一格式为dmatest: result channel: test id: error msg with src_offval dst_offval lenval (err code)实际输出示例% dmesg | tail -n 1 dmatest: result dma0chan0-copy0: #1: No errors with src_off0x7bf dst_off0x8ad len0x3fea (0)几个要点消息格式对不同类型的错误是统一的括号里的数字是附加信息具体含义视错误类型而定错误码、错误计数或状态线程命名规则是通道名-操作序号操作取copy/set/xor/pq四种序号是线程下标对应 dmatest_add_threads() 中kthread_create(dmatest_func, thread, %s-%s%u, ...)的命名每个测试线程结束时还会打印一行汇总包含执行的测试数、失败数和结果码% dmesg | tail -n 1 dmatest: dma0chan0-copy0: summary 1 test, 0 failures 1000 iops 100000 KB/s (0)汇总行额外给出 iops 与吞吐KB/s由 dmatest_func() 退出路径 根据总耗时、总传输字节数计算源码中用定点数运算保留两位小数的 iops。数据内容不匹配data miscompare的细节信息不走上述统一格式而是由 dmatest_mismatch() 逐字节打印分类告警最多打印 32 条MAX_ERROR_COUNT 32超出部分只打印被抑制的条数。这些告警可以直接定位错误类型srcbuf[0x..] overwritten!—— 源缓冲区被 DMA 引擎改写了严重问题dstbuf[0x..] not copied!—— 应拷贝的区域没被写入dstbuf[0x..] was copied!—— 不该写的区域被写了越界拷贝dstbuf[0x..] mismatch!—— 数据内容错误。此外还有非数据类结果消息test timed out在timeout毫秒内未完成、prep errordevice_prep_dma_*返回 NULL、submit errortx_submit失败、completion error status/completion busy status完成状态不是DMA_COMPLETE、unmap data NULL、src/dst mapping errorDMA 映射失败均可直接通过 dmesg 的 result 行观察到。通道分配与释放对应文档 Part 5分配通道不需要在启动测试前配置通道。直接运行而未配置通道时dmatest 会测试所有可用通道% echo 1 /sys/module/dmatest/parameters/run dmatest: No channels configured, continue with any用channel参数按名字请求通道。请求成功后通道即被注册并有一个 pending 线程加入测试列表% echo dma0chan2 /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan2重复上述操作可追加更多通道。回读channel参数只会返回最后一个成功加入的通道名源码 dmatest_chan_get() 确认测试未在运行也无 pending 时会清理测试列表并清空该字符串% echo dma0chan1 /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan1 % echo dma0chan2 /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan2 % cat /sys/module/dmatest/parameters/channel dma0chan2请求空字符串则请求所有可用通道% echo /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan0 dmatest: Added 1 threads using dma0chan3 ...测试配置的任何时刻读取test_list参数都会打印当前 pending 的测试列表% cat /sys/module/dmatest/parameters/test_list dmatest: 1 threads using dma0chan0 dmatest: 1 threads using dma0chan3 ...重要通道配置不会跨测试运行保留——每次新的测试运行都要重新配置通道。另外dmatest_chan_set() 还实现了几个保护逻辑解释了上面行为的原因写入新通道名时若该通道已在 pending 列表中会返回-EBUSY并回滚参数值若请求的通道实际添加失败如名字不存在参数值会被恢复为上一个成功加入的通道名避免用户误以为新通道已生效。释放通道把run置 0 即可释放通道% echo dma0chan1 /sys/module/dmatest/parameters/channel dmatest: Added 1 threads using dma0chan1 % cat /sys/class/dma/dma0chan1/in_use 1 % echo 0 /sys/module/dmatest/parameters/run % cat /sys/class/dma/dma0chan1/in_use 0这里的/sys/class/dma/chan/in_use属性由 DMAEngine 框架核心提供在 dmaengine.c 中实现为直接输出chan-client_count客户端持有计数因此它可以客观反映 dmatest 是否仍占用着该通道是验证“通道是否真正释放”的可靠手段。前一次测试成功结束后分配的通道会在下一次请求新通道时自动释放——对应源码中dmatest_chan_set()开头if (!is_threaded_test_run(info) !is_threaded_test_pending(info)) stop_threaded_test(info);的清理逻辑。源码纵深测试数据模式与校验原理dmatest 的校验能力来自精心设计的位模式。每个字节中bit 7源缓冲区恒为 1PATTERN_SRC 0x80目标缓冲区恒为 0PATTERN_DST 0x00bit 6PATTERN_COPY 0x40标记“将被 DMA 拷贝覆盖”的字节bit 5PATTERN_OVERWRITE 0x20标记“将被 DMA 覆写”的字节目标侧低 5 位PATTERN_COUNT_MASK 0x1f按字节地址递增的计数器的反码。memset 测试对源缓冲区使用固定索引PATTERN_MEMSET_IDXmemset 的值由源缓冲区第一个字节决定见 dmatest_func() 中device_prep_dma_memset(chan, ..., *(src-aligned[0] src-off), len, flags)的取值。每次迭代中dmatest_init_srcs()/dmatest_init_dsts()按[0, off)、[off, offlen)、[offlen, buf_size)三段分别填充“背景/被拷贝/背景”模式。传输完成后dmatest_verify() 对源、目标两侧各三段逐字节比对任何一字节不符即归类为前述四种错误之一。这就是文档所说“验证 DMA 引擎只拷贝了请求区域、字节没有错位、源缓冲区未被修改”的实现基础只要 DMA 引擎多拷、少拷、越界写或错位写位模式的某个 bit 必然与期望值冲突。传输路径本身也值得了解dmatest_func() 主循环计算本次长度与偏移默认用get_random_bytes()产生随机 len 和随机 src/dst 偏移并按对齐要求向下取整alignment-1时取设备声明的copy_align/fill_align/xor_align/pq_alignnorandom则固定全缓冲零偏移transfer_size则锁定长度用dma_map_page()映射缓冲区目标侧特意用DMA_BIDIRECTIONAL强制 writeback/invalidate按操作类型调用device_prep_dma_memcpy/memset/xor/pqXOR/PQ 源数量会夹取在设备能力max_xor/maxpq之内且强制为奇数以保证 dst 与 src 数量一致min_odd()完成通知二选一默认走tx-callback中断回调 wait_event_freezable_timeout()受timeout限制-1 表示无限等待polled1时改用dma_sync_wait()轮询源码中若设备声明DMA_COMPLETION_NO_ORDER会强制关闭 polled 并告警校验通过后统计 iops/吞吐退出前打印 summary若有失败还会调用dmaengine_terminate_sync()终止该通道上所有在途传输。线程方面每个通道-操作序号线程在启动时设置set_user_nice(current, 10)最低实时优先级、高于普通用户态避免测试线程与生产负载互抢 CPU且标记set_freezable()可被系统冷冻。实践建议与适用前提验收驱动正确性modprobe dmatest run1 iterationsN wait1后检查 dmesg 中 summary 行的失败数是否为 0压测/性能摸底加verbose1观察单次结果或noverify1跳过校验此时 result 行只在 verbose 下打印以逼近纯吞吐对齐敏感型硬件用alignment显式指定 2 的幂对齐用transfer_size固定传输长度排除随机因素便于复现无中断环境如某些仿真平台polled1走轮询完成IOMMU/弹跳缓冲干扰排查nobounce1强制使用GFP_DMA缓冲适用前提目标平台已启用CONFIG_DMA_ENGINE且 DMA 驱动已注册具备前述四种能力之一的通道可用ls -1 /sys/class/dma/确认timeout默认 3000 ms慢速控制器或大缓冲传输需自行调大。完整的官方操作说明见 dmatest.rstDMA 框架客户端/驱动编写参考 client.rst 与 provider.rst测试模块源码见 dmatest.c。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考