4
votes

I have developped a kernel module to manage a nf4 tag as a char device.

I have developped this module outside of the kernel and tested it compiled as a loadable kernel module (i.e. .ko) during the development phase.

Once the driver has been functional and stable enough I have inserted it into linux kernel source (v4.9.30) using patches so it's built as part of the kernel.

Here I'm in the situation where the module is loaded probed at boot by kernel as it's builtin and it appears into the device tree.

Now, I would like to try some improvements on the driver and I don't want to implement those changes directly into the kernel.

So I would like to keep the driver's code integrated into linux kernel but not having it inserted at boot. To do this I have just changed the driver's status field with status = "disable"; into the device-tree and indeed the module is not inserted anymore at boot.

However I can't insert the loadable module which as been modified. I'm having a ENODEV at insertion which is due to the platform_device not beeing found into the probe function.

What I don't understand is why the platform device is not beeing found when the device-tree hasn't been changed except for status field value.


EDIT: Add precisions about the situation

After some more exploring I have to precise that I'm not even entering the callback nf4_probe.

After checking platform_driver_probe implementation (see here) into v4.9.30 kernel source it seems that the error comes from here:

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

By checking the device-tree from the command line I can see that the device is defined as the directory /proc/device-tree/nf4tag exists and is filled with values corresponding to those into the device-tree.


EDIT: Add precisions about the aim of the question after @sawdust's answer

I was apparently misunderstanding that status=disable meant that the device wasn't present at all on the hardware configuration. Though it was just describing weither the driver should be probed or not.

To make my objective clearer I precise that I do have the driver coded as proper module and compiled as a loadable module for the kernel I'm using.

However I don't want to recompile the kernel to test every change I do. So my aim is to recompile only the .ko until my modifications are made and then, once everything has been done, add those modifications to the builtin moduleusing a patch.

With this way of working I can just rebuild the .ko and insert it on my target platform instead of recompiling a kernel for each modification.

So to resume my question should have been:

How to replace a builtin module with a loadable one without recompiling kernel to disable the builtin module?

Maybe there isn't a solution for this aim except disabling builtin module compiling onto kernel.

1
Sounds like you have two versions of your driver: one is a loadable module and built out-of-tree, and another version for a built-in that is in the kernel source tree?sawdust
Yes, I have an out-of-tree driver which I used during development (compiled as .ko). Once finished I have patched this driver into the kernel sources. Now I'm implementing new functionalities and I would like to keep the same kernel without having to delete the driver sources which have been integrated.Arkaik

1 Answers

5
votes

What I don't understand is why the platform device is not beeing found when the device-tree hasn't been changed except for status field value.

You seem to misunderstand what the status = "disable" attribute actually means.
Besides it meaning that the kernel should "not having it inserted at boot", a disabled node means that the device is not part of the current hardware configuration at all.
The driver, whether built-in or loadable module, is simply not to be probed because it has been disabled for the current configuration.

If you want your driver, whether built-in or loadable module, to be in the current configuration, then have a status = "okay" attribute in its Device Tree node.

IOW the Device Tree is for describing the current hardware configuration to the kernel.
Do not try to use the Device Tree to control loadable modules (since it cannot).

Here I'm in the situation where the module is loaded at boot by kernel as it's builtin and it appears into the device tree.

This statement makes no sense, as you seem to be describing your driver simultaneously as a built-in module as well as a loadable module.
A built-in driver does not have to be "loaded" in order to invoke its probe routine.
Because a driver can be either built-in or loadable, "loading" and "probing" are two distinct phases, and should not be conflated.

So I would like to keep the driver's code integrated into linux kernel but not having it inserted at boot.

You seem to be conflating the concept of the Linux kernel with the source code tree.
"Driver's code integrated into linux kernel" would typically be interpreted to mean a built-in driver, that is the driver is linked in and part of the kernel image that is loaded at boot.
Whereas driver code that is stored in the kernel source tree confers no designation has to whether it is a built-in or a loadable module. Many drivers (and other types of modules) can be built as either, and it is the build configuration that specifies which.

If you want your driver to be a loadable module, then (instead of changing the Device Tree):

a. you need to code your driver as a proper module;
b. you need to modify the Kconfig file to choose between a built-in or loadable module (i.e. a tristate versus bool selection specification).
c. configure the kernel to build your driver as a loadable module.


A device driver that is a loadable module and defined in the Device Tree could still be automatically loaded and probed during boot. You may have to use module blacklisting to prevent that.


** ADDENDUM **

How to replace a builtin module with a loadable one without recompiling kernel to disable the builtin module?

You can't. That's why Kconfig forces you to choose between a loadable module (m) or built-in (y) if you want that driver to be built.

... then, once everything has been done, add those modifications to the builtin moduleusing a patch.

This makes no sense, since you only need a single copy of the driver source to build either a loadable module or a built-in version of the driver.

With this way of working I can just rebuild the .ko and insert it on my target platform instead of recompiling a kernel for each modification.

Seems like how you integrated your driver into the kernel source is questionable.
What have you actually done to integrate your driver into the kernel source tree?
Which Kconfig and Makefile did you modify for your driver?
What new CONFIG_* symbol(s) did you create for your driver?

Yes, you have to "recompile for each modification", but make is smart enough to rebuild only what is necessary. You could further shorten the kernel rebuild time by using make modules when you know that only your loadable driver has been modified.


TO CONCLUDE

  • It's not possible to use out-of-tree loadable module without recompiling the kernel to disable the builtin one.

BUT

  • Recompiling kernel only once with tristate at M and blacklisting the module on kernel boot line succeed.
  • Recompiling kernel only once with tristate at n succeed.

So the kernel has to be recompiled at least once, but then it's possible to work with an out-of-tree driver compiled as loadable module without having to remove the code integrated into linux sources.