【问题标题】:Replace kernel builtin module with a loadable one用可加载的模块替换内核内置模块
【发布时间】:2018-12-24 11:08:40
【问题描述】:

我已经开发了一个内核模块来管理一个 nf4 标签作为一个字符设备。

我在内核之外开发了这个模块,并在开发阶段将其编译为可加载的内核模块(即 .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 并将其插入到我的目标平台上,而不是为每次修改重新编译内核。

所以要继续我的问题应该是:

如何在不重新编译内核的情况下将内置模块替换为可加载模块以禁用内置模块?

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

【问题讨论】:

  • 听起来您的驱动程序有两个版本:一个是可加载模块并在树外构建,另一个版本是内核源代码树中的内置模块?
  • 是的,我有一个在开发过程中使用的树外驱动程序(编译为 .ko)。完成后,我已将此驱动程序修补到内核源代码中。现在我正在实现新功能,我想保留相同的内核,而不必删除已集成的驱动程序源。

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


【解决方案1】:

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

您似乎误解了 status = "disable" 属性的实际含义。
除了它意味着内核应该“在启动时不插入它”,禁用的节点意味着该设备根本不是当前硬件配置的一部分。
驱动程序,无论是内置模块还是可加载模块,都不会被探测,因为它已针对当前配置禁用。

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

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

这里我的情况是模块在启动时由内核加载,因为它是内置的,并且它出现在设备树中。

这种说法毫无意义,因为您似乎同时将驱动程序描述为内置模块和可加载模块。
无需“加载”内置驱动程序即可调用其探测例程。
因为驱动程序可以是内置的或可加载的,所以“加载”和“探测”是两个不同的阶段,不应混为一谈。

所以我想将驱动程序的代码集成到 linux 内核中,但不让它在启动时插入。

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

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

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


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


** 附录 **

如何在不重新编译内核以禁用内置模块的情况下将内置模块替换为可加载模块?

你不能。这就是为什么 Kconfig 会强制您在可加载模块 (m) 或内置 (y) 之间进行选择(如果您希望构建该驱动程序)。

...然后,一旦一切都完成了,使用补丁将这些修改添加到内置模块中。

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

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

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

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


总结

  • 如果不重新编译内核以禁用内置模块,则无法使用树外可加载模块。

但是

  • M 处使用tristate 仅重新编译内核一次并将内核引导行上的模块列入黑名单成功。
  • n 使用tristate 重新编译内核一次成功。

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

【讨论】:

  • 你说得对,我不是很清楚,我也没有为我的目标使用正确的解释。我试图让它更清楚
猜你喜欢
  • 1970-01-01
  • 2012-08-20
  • 2016-01-05
  • 1970-01-01
  • 1970-01-01
  • 2013-03-10
  • 2012-03-14
  • 1970-01-01
  • 2018-03-09
相关资源
最近更新 更多