
Linux 内核 KUnit clk API测试时钟提供者与消费者的受管资源 API 全解析【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux本文基于内核文档 clk.rst 及其引用的实现 clk_kunit_helpers.c系统讲解 Linux 内核 KUnit 时钟 API 的完整函数族、资源自动管理原理与典型用法。读完本文你将掌握如何在 KUnit 单元测试中安全地注册/获取/启停时钟并理解这些 helper 如何借助 KUnit 资源框架在测试结束时自动完成清理避免时钟引用泄漏和 provider 残留。1. KUnit clk API 解决什么问题KUnit 是内核自带的轻量单元测试框架常用于验证驱动框架代码而非真实硬件行为。clk.rst 开篇即点明其定位The KUnit clk API is used to test clk providers and clk consumers. KUnit clk API 用于测试 clk 的提供者与消费者。要测试时钟框架测试代码通常必须完成两组操作消费者侧clk_get()/of_clk_get()/clk_hw_get_clk()获取struct clk再clk_prepare_enable()启用提供者侧clk_hw_register()/of_clk_hw_register()注册struct clk_hw或用of_clk_add_hw_provider()挂接设备树的 phandle 解析回调。如果这些调用在测试里手动配对clk_put()、clk_disable_unprepare()、clk_hw_unregister()、of_clk_del_provider()一旦断言提前失败、测试中途退出清理代码就会被跳过造成时钟引用泄漏或全局 provider 表残留污染后续用例。KUnit clk API 就是为消除这种手工配对而设计的“测试受管”test managed版本。值得注意的是这份文档本身并不重复罗列 API 说明而是通过 Sphinx 指令直接从源码导出内核文档.. kernel-doc:: drivers/clk/clk_kunit_helpers.c :export:也就是说文档内容即 clk_kunit_helpers.c 中每个函数的/** ... */注释。这种写法保证了 API 文档与实现永远同步——这也是后文所有接口说明与源码逐条对应的依据。该文档同时被 API Reference 索引 归入 “Driver KUnit API” 一节与 of、platformdevice 等 API 并列。2. 完整 API 一览include/kunit/clk.h 声明了全部 8 个测试受管接口。按职责可分为消费者侧 5 个、提供者侧 3 个函数受管的底层 API返回值clk_get_kunit(test, dev, con_id)clk_get()struct clk *失败返回ERR_PTRof_clk_get_kunit(test, np, index)of_clk_get()struct clk *失败返回ERR_PTRclk_hw_get_clk_kunit(test, hw, con_id)clk_hw_get_clk()struct clk *失败返回ERR_PTRclk_hw_get_clk_prepared_enabled_kunit(test, hw, con_id)clk_hw_get_clk()clk_prepare_enable()prepared/enabled 的struct clk *失败返回ERR_PTRclk_prepare_enable_kunit(test, clk)clk_prepare_enable()0 成功负 errno 失败clk_hw_register_kunit(test, dev, hw)clk_hw_register()0 成功负 errno 失败of_clk_hw_register_kunit(test, node, hw)of_clk_hw_register()0 成功负 errno 失败of_clk_add_hw_provider_kunit(test, np, get, data)of_clk_add_hw_provider()0 成功负 errno 失败参数语义与底层 API 完全一致test是 KUnit 测试上下文struct kunit *dev是时钟消费者对应的设备np是设备树节点struct device_node *index是clocks属性中的索引hw是硬件时钟描述struct clk_hwcon_id是连接 IDget是 phandle 参数到struct clk_hw *的解码回调data是传给回调的上下文。每个函数的文档注释都强调同一句契约“Just like XXX(), except the … is managed by the test case”即行为与底层 API 一致唯一区别是生命周期由测试用例接管。3. 实现原理deferred action 与逆序清理所有 8 个接口共用同一套机制源码非常精炼值得逐行看。3.1 KUNIT_DEFINE_ACTION_WRAPPER 宏clk_kunit_helpers.c 顶部就定义了两类清理包装器KUNIT_DEFINE_ACTION_WRAPPER(clk_disable_unprepare_wrapper, clk_disable_unprepare, struct clk *); ... KUNIT_DEFINE_ACTION_WRAPPER(clk_put_wrapper, clk_put, struct clk *); ... KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_wrapper, clk_hw_unregister, struct clk_hw *); ... KUNIT_DEFINE_ACTION_WRAPPER(of_clk_del_provider_wrapper, of_clk_del_provider, struct device_node *);该宏定义于 resource.h#define KUNIT_DEFINE_ACTION_WRAPPER(wrapper, orig, arg_type) \ static void wrapper(void *in) \ { \ arg_type arg (arg_type)in; \ orig(arg); \ }宏的意义在于KUnit 的 deferred action 统一接受void *参数直接做函数指针强转会破坏控制流完整性CFI甚至导致崩溃——头文件注释明确指出“casting function pointers will break control flow integrity (CFI), leading to crashes”因此官方推荐用此宏生成类型安全的静态包装函数再交给资源框架。3.2 kunit_add_action 的清理时机包装器最终通过kunit_add_action_or_reset()注册进测试上下文。按 resource.h 对kunit_add_action()的说明动作被延迟到测试退出时执行无论测试正常结束还是因断言失败提前中止多个动作按注册的相反顺序执行栈式 LIFO。这一点在clk_hw_get_clk_prepared_enabled_kunit()中体现得最充分它先注册clk_put清理来自clk_hw_get_clk_kunit再注册clk_disable_unprepare清理来自clk_prepare_enable_kunit。测试结束时逆序执行即先 disable/unprepare再 put——恰好与手动配对要求的顺序一致且即便中途断言失败也不会遗漏。3.3 错误路径的行为以__clk_get_kunit()为核心clk_kunit_helpers.c#L38-L50static struct clk *__clk_get_kunit(struct kunit *test, struct clk *clk) { int ret; if (IS_ERR(clk)) return clk; /* 获取失败错误指针原样透传 */ ret kunit_add_action_or_reset(test, clk_put_wrapper, clk); if (ret) return ERR_PTR(ret); /* 注册清理失败返回 ERR_PTR */ return clk; }三个 get 系列接口clk_get_kunit、of_clk_get_kunit、clk_hw_get_clk_kunit都是“先调用底层 API再交给__clk_get_kunit托管”的两行结构行为完全统一底层失败则透传原始错误托管失败则返回新的ERR_PTR。组合接口clk_hw_get_clk_prepared_enabled_kunit()clk_kunit_helpers.c#L137-L153等价于文档注释中给出的这段代码struct clk *clk clk_hw_get_clk(...); clk_prepare_enable(clk);其实现先走clk_hw_get_clk_kunit()再走clk_prepare_enable_kunit()若 prepare 阶段失败clk_put清理仍会注册生效返回ERR_PTR(ret)不会留下悬空引用。clk_prepare_enable_kunit()clk_kunit_helpers.c#L23-L33则是“先 enable、后挂清理”的标准两步int clk_prepare_enable_kunit(struct kunit *test, struct clk *clk) { int ret; ret clk_prepare_enable(clk); if (ret) return ret; return kunit_add_action_or_reset(test, clk_disable_unprepare_wrapper, clk); }提供者侧三个接口是同一模式的镜像clk_hw_register_kunit()、of_clk_hw_register_kunit()在底层注册成功后挂clk_hw_unregister_wrapper清理of_clk_add_hw_provider_kunit()clk_kunit_helpers.c#L222-L233在of_clk_add_hw_provider()成功后挂of_clk_del_provider_wrapper测试结束时自动从该设备节点删除 provider。4. 消费者侧与提供者侧的典型用法基于上述语义一个“提供者 消费者”联动的 KUnit 时钟测试典型骨架如下示例代码示意受管 API 的调用组合static void clk_provider_consumer_example(struct kunit *test) { /* 1. 注册硬件时钟测试结束自动 clk_hw_unregister() */ int ret clk_hw_register_kunit(test, test-priv, hw); KUNIT_ASSERT_EQ(test, ret, 0); /* 2. 以消费者身份获取并直接启用 * 测试结束自动 clk_disable_unprepare() clk_put() */ struct clk *clk clk_hw_get_clk_prepared_enabled_kunit( test, hw, core); KUNIT_EXPECT_NOT_ERR(test, clk); /* 3. 断言运行速率等 */ KUNIT_EXPECT_EQ(test, clk_get_rate(clk), EXPECTED_RATE); /* 无需任何手动 put/unprepare/unregister 代码 */ }要点若测试只拿到裸的struct clk_hw例如 fixed-rate 场景clk_hw_get_clk_prepared_enabled_kunit()是获取 启用一步到位的最省写法设备树路径下of_clk_hw_register_kunit()用device_node替代device完成注册of_clk_get_kunit(test, np, index)按clocks属性索引取时钟需要按 phandle 动态解析时用of_clk_add_hw_provider_kunit()注册自己的get回调测试结束 provider 自动删除不会污染全局设备树时钟查找表。5. 编译与运行前提时钟框架自带的 KUnit 用例由 Kconfig 控制在 drivers/clk/Kconfig 中可以看到config CLK_KUNIT_TEST tristate Basic Clock Framework Kunit Tests if !KUNIT_ALL_TESTS depends on KUNIT default KUNIT_ALL_TESTS select DTC help Kunit tests for the common clock framework. config CLK_FIXED_RATE_KUNIT_TEST tristate Basic fixed rate clk type KUnit test if !KUNIT_ALL_TESTS因此运行这些测试的前提是开启CONFIG_KUNIT或CONFIG_KUNIT_ALL_TESTS时钟框架基础测试、fixed-rate 类型测试随之可用CLK_KUNIT_TEST还显式select DTC说明部分用例依赖设备树编译工具链。开启后可按 KUnit 的通用流程在内核构建/测试运行器中执行更多说明见 KUnit 使用文档 与 运行指南。从源码结构看helper 文件末尾带有MODULE_LICENSE(GPL)与MODULE_DESCRIPTION()即该实现以可加载模块形式构建所有 8 个接口均EXPORT_SYMBOL_GPL其他模块中的 KUnit 测试用例可直接链接这些符号。6. 小结与源码索引KUnit clk API 的设计可以浓缩为一句话用 KUnit 资源框架的 deferred action 把时钟的注册、获取、启停的生命周期绑定到测试用例上让清理必然发生且顺序正确。对驱动开发者而言这意味着写时钟框架测试时只需关注断言逻辑资源管理交给*_kunit()后缀的受管版本即可。关键路径速查内容路径API 文档kernel-doc 自动导出Documentation/dev-tools/kunit/api/clk.rst实现8 个受管接口 清理包装器drivers/clk/clk_kunit_helpers.c对外声明头文件include/kunit/clk.hKUNIT_DEFINE_ACTION_WRAPPER与kunit_add_action定义include/kunit/resource.h时钟框架 KUnit 用例配置drivers/clk/KconfigDriver KUnit API 总索引Documentation/dev-tools/kunit/api/index.rst【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考