用可加载的模块替换内核内置模块

时间:2018-07-16 15:31:05

标签: linux-kernel linux-device-driver kernel-module device-tree

我已经开发了一个内核模块来将nf4标签作为char设备进行管理。

我已经在内核外部开发了该模块,并在开发阶段对其进行了测试,并将其编译为可加载的内核模块(即.ko)。

一旦驱动程序运行正常且足够稳定,我便使用补丁将其插入linux内核源代码(v4.9.30),使其成为内核的一部分。

在这种情况下,由于模块是内置的,因此会在启动时由内核加载进行探测,并且它出现在设备树中。

现在,我想在驱动程序上进行一些改进,但我不想直接在内核中实现这些更改。

因此,我想将驱动程序的代码集成到linux内核中,但不要在启动时将其插入。 为此,我刚刚将status = "disable";的驱动程序状态字段更改为设备树,实际上在启动时不再插入该模块。

但是,我无法插入已修改的可加载模块。我插入ENODEV是由于在探测功能中未找到platform_device。

我不明白的是,为什么除了状态字段值以外没有更改设备树时,为什么找不到平台设备?


编辑: 添加有关情况的精确度

经过更多的探索之后,我不得不精确地说,我什至没有进入回调nf4_probe

在将platform_driver_probe的实现(请参阅here)检查到v4.9.30内核源代码之后,错误似乎出在这里:

if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
    retval = -ENODEV;

通过从命令行检查设备树,我可以看到该设备已定义为目录/proc/device-tree/nf4tag存在,并用与设备树相对应的值填充。


编辑: 在@sawdust的答案之后添加有关问题目的的精度

我显然误会为status=disable意味着该设备在硬件配置上根本不存在。尽管只是描述了是否应该探测驱动程序。

为了更清楚地说明我的目标,我明确指出我已将驱动程序编码为适当的模块,并已编译为所用内核的可加载模块。

但是,我不想重新编译内核以测试我所做的每个更改。因此,我的目标是仅重新编译.ko,直到进行修改为止,然后,在完成所有操作后,使用补丁将这些修改添加到内置模块中。

通过这种工作方式,我可以重建.ko并将其插入到我的目标平台上,而不必为每次修改重新编译内核。

因此,恢复我的问题应该是:

如何用可加载模块替换内置模块而无需重新编译内核以禁用内置模块?

除了禁用内置模块编译到内核之外,也许没有其他解决方案。

1 个答案:

答案 0 :(得分:4)

  

我不明白的是,为什么除了状态字段值以外没有更改设备树时,为什么找不到平台设备?

您似乎误解了status = "disable"属性的实际含义。
除了表示内核应该“不将其插入引导” 之外,禁用的节点还意味着该设备根本不属于 current 硬件配置的一部分。 /> 不管是内置模块还是可加载模块,都不会对其进行探测,因为当前配置已禁用该驱动程序。

如果希望驱动程序(无论是内置模块还是可装入模块)处于当前配置中,则在其“设备树”节点中具有status = "okay"属性。

IOW设备树用于描述内核的当前硬件配置。
不要尝试使用设备树来控制可加载模块(因为它不能)。

  

在这种情况下,由于模块是内置的,因此会在启动时由内核加载,并且出现在设备树中。

该语句没有意义,因为您似乎同时将驱动程序描述为内置模块和可加载模块。
不必“加载” 内置驱动程序即可调用其探测例程。
由于驱动程序可以是内置的,也可以是可加载的,因此“加载”和“探测”是两个不同的阶段,不应混为一谈。

  

因此,我想将驱动程序的代码集成到linux内核中,但不要在启动时将其插入。

您似乎正在将Linux内核的概念与源代码树混为一谈。
“将驱动程序的代码集成到linux内核中” 通常会解释为内置驱动程序,即该驱动程序被链接到内核中,并在引导时加载了内核映像的一部分。
存储在内核源代码树中的驱动程序代码无需指定是内置模块还是可加载模块。可以构建许多驱动程序(以及其他类型的模块),而构建配置指定了哪个。

如果您希望驱动程序成为可加载模块,那么(而不是更改设备树):

a。您需要将驱动程序编码为适当的模块;
b。您需要修改 Kconfig 文件以在内置模块或可加载模块之间进行选择(即tristatebool选择规范)。
C。配置内核以将驱动程序构建为可加载模块。


在设备树中定义的作为可加载模块的设备驱动程序仍可以在引导过程中自动加载和探测。 您可能必须使用模块黑名单来防止这种情况。


**附录**

  

如何用可加载模块替换内置模块而无需重新编译内核以禁用内置模块?

不能。这就是为什么Kconfig强迫您在要构建驱动程序的情况下选择可加载模块(m)或内置模块(y)。

  

...,然后,在完成所有操作后,使用补丁将这些修改添加到内置模块中。

这没有任何意义,因为您只需要驱动程序源的一个副本即可构建可加载模块或驱动程序的内置版本。

  

通过这种工作方式,我可以只重建.ko并将其插入到我的目标平台上,而不必为每次修改重新编译内核。

似乎如何将驱动程序集成到内核源中是有问题的。
将驱动程序集成到内核源代码树中,您实际上做了什么?
您为驱动程序修改了哪个 Kconfig Makefile
您为驱动程序创建了哪些新的CONFIG_ *符号?

是的,您必须“为每个修改重新编译” ,但是 make 足够聪明,可以仅重建必要的内容。当您知道只有可加载的驱动程序已被修改时,可以使用make modules进一步缩短内核的重建时间。


要结论

  • 在不重新编译内核以禁用内置模块的情况下,无法使用树外可加载模块。

但是

  • 仅在tristateM重新编译一次内核,并将该模块列入内核引导行成功。
  • 仅在tristate处使用n重新编译内核一次。

因此内核必须至少重新编译一次,但是可以使用编译为可加载模块的树外驱动程序工作,而不必删除集成到linux源代码中的代码。