With the imminent release of kmod 33, I thought it’d be good to have a post about the different types of module dependencies that we have in the Linux kernel and kmod. The new version adds another type, weak dependency, and as the name implies, is the weakest of all. But let’s revisit what are the other types first.
Hard (symbol) dependency
This is the first dependency that every appeared in kmod (and
module-init-tools). A hard (or as some call, “symbol”) dependency occurs when
your module calls or uses an exported symbol of another module. The most common
way is by calling a function that is exported in another module. Example: the
xe.ko
calls a function ttm_bo_pin()
that is provided and exported by
another module, ttm.ko
. Looking to the source:
$ nm build/drivers/gpu/drm/ttm/ttm.ko | grep -e "\bttm_bo_pin\b"
0000000000000bc0 T ttm_bo_pin
$ nm build64/drivers/gpu/drm/xe/xe.ko | grep -e "\bttm_bo_pin\b"
U ttm_bo_pin
It is not possible to insert the xe.ko module before ttm.ko and if you try
via insmod (that doesn’t handle dependencies) it will fail with the kernel
complaining that the ttm_bo_pin
symbol is undefined.
The manual invocations to nm
illustrates what the depmod tool does: it
opens the modules and reads the ELF headers. Then it takes note of all the
symbols required and provided by each module, creating a graph of symbol
dependencies. Ultimately that leads to module dependencies: xe ➛ ttm.
This is recorded in the modules.dep
file and its sibling modules.dep.bin
.
The former is human-readable and the latter is used by libkmod, but they
contain the same information: all dependencies for all the modules. Also note
that each line reflects indirect dependencies: module A calls symbol from B
and B calls symbol from C will lead to A depending on both B and C. Real world example:
$ cat /lib/modules/$(uname -r)/modules.dep | grep kernel/drivers/gpu/drm/xe/xe.ko.zst:
kernel/drivers/gpu/drm/xe/xe.ko.zst: kernel/drivers/gpu/drm/drm_gpuvm.ko.zst kernel/drivers/gpu/drm/drm_exec.ko.zst kernel/drivers/gpu/drm/scheduler/gpu-sched.ko.zst kernel/drivers/gpu/drm/drm_buddy.ko.zst kernel/drivers/i2c/algos/i2c-algo-bit.ko.zst kernel/drivers/gpu/drm/drm_suballoc_helper.ko.zst kernel/drivers/gpu/drm/drm_ttm_helper.ko.zst kernel/drivers/gpu/drm/ttm/ttm.ko.zst kernel/drivers/gpu/drm/display/drm_display_helper.ko.zst kernel/drivers/media/cec/core/cec.ko.zst kernel/drivers/acpi/video.ko.zst kernel/drivers/platform/x86/wmi.ko.zst
So the xe.ko
(with .zst extension since it’s compressed) directly or indirectly
depends on drm_gpuvm.ko, drm_exec.ko, gpu-sched.ko, drm_buddy.ko, i2c-algo-bit.ko,
drm_suballoc_helper.ko, drm_ttm_helper.ko, ttm.ko, drm_display_helper.ko, cec.ko,
video.ko, wmi.ko
. Same information, but using the kmod tools rather than looking
at the raw index:
$ modinfo -F depends xe
drm_display_helper,ttm,drm_gpuvm,drm_suballoc_helper,video,drm_buddy,drm_exec,drm_ttm_helper,gpu-sched,cec,i2c-algo-bit
Soft dependency
There are situations in the kernel where it’s not possible or desired to use a symbol directly from another module - they may interact by registering in a subsystem, scanning a bus etc.. In this case depmod doesn’t have enough information from the ELF file about that. Yet the user would have a more complete support if both modules were available - it may even cause failures visible to the end user and not only “partial support for features”.
The softdep implementation contains 2 parts: pre and post dependencies. The post dependencies are not very much used in practice: they instruct kmod to load another module after loading the target one.
They can come from a configuration file like e.g. /etc/modprobe.d/foo.conf
or
from the kernel itself by embedding that info in the module. From the kernel source
this is achieved by using the macro MODULE_SOFTDEP()
. Example:
lib/libcrc32c.c:MODULE_SOFTDEP("pre: crc32c");
When libkmod is loading a module it will first load, in order:
- hard dependencies
- soft pre dependencies
- target module
- soft post dependencies
Historically softdeps were also a way to (mostly) get rid of install rules, in
which the configuration instructs libkmod to execute something instead of loading
the module - people would add an install rule to execute something and then call
modprobe again with --ignore-install
to fake a dependency. That could easily
lead to a runtime loop which is avoided with softdep since kmod can (and does)
check for loops.
Weak dependency
After explaining the other types of dependencies, back to the new addition in kmod 33. These are very similar to pre softdep: they come either from a configuration file or embedded in the module and they express a dependency that wouldn’t cause the target module to fail to load, but that may cause the initialization to export less features or fail while initializing. There is one important difference: weak dependencies don’t cause libkmod to actually load the module. Rather the dependency information may be used by tools like dracut and other tools responsible for assembling an initrd to make the module available since it may or may not be used. Why are they called “weak”? This was a borrowed terminology from “weak symbols”: a weak symbol is there, waiting to be used, but it may or may not be, with the final decision happening in the final link stage.
With weak dependencies, hopefully some of the pre softdep embedded in the kernel
may be replaced: if the target module is already doing a request_module()
or
in some way getting the other module to be loaded, it doesn’t need a softdep that
would serialize the module load order and possibly load more modules than required.