Overview
Unbreakable Enterprise Kernel Release 8 (UEK8) has a wide range of kernel modules that serve diverse user needs. While some modules are crucial for core functionality, others might be only suited for desktop users or even some of them can be deprecated.
To address this variability, UEK8 offers multiple packages for modules, such as kernel-uek-modules-desktop
, kernel-uek-modules-core
, kernel-uek-modules-deprecated
and so on. More information about the full list of packages is detailed in this blog. [1]
This blog focuses on exploring the internals of packaging these kernel modules into different sub-packages, with a primary focus on what problems this newer approach is solving and what UEK developers need to know when they want to work with UEK8’s packaging.
Directory Layout of UEK8’s uek-rpm
Oracle Linux supports UEK8 on both Oracle Linux 9 and Oracle Linux 10.
The uek-rpm
directory plays an important role in two major downstream concepts – UEK RPM packaging and kernel configuration. Below is a sample of the layout of the uek-rpm
directory:
uek-rpm/ ├── ol10 │ ├── ... ├── ol9 │ ├── config-aarch64 # Kernel config for ARM architecture builds │ ├── config-aarch64-container # Kernel config for ARM architecture container builds │ ├── config-aarch64-debug # Kernel config for ARM architecture debug builds │ ├── config-x86_64 # Kernel config for x86_64 architecture builds │ ├── config-x86_64-container # Kernel config for x86_64 architecture container builds │ ├── config-x86_64-debug # Kernel config for x86_64 architecture debug builds │ ├── denylist.txt.S # denylist file which is a list of modules for blacklisting │ ├── kernel-uek.spec # Kernel spec file that is responsible of RPM packaging │ ├── mod-sign.sh # Script for signing the modules │ └── modules.yaml.S # Modules listing yaml file which deals with which package should each module reside └── tools └── filter-modules.py # Python script responsible for all the filtering based on the listing in modules.yaml.S
For simplicity the OL10 files are not shown in the above diagram, but they are similar to OL9. Similarly, other non-packaging related files are also not listed.
Organizing Kernel Modules in UEK8
The old format (in UEK7 and older) had two files – a core list and an extra list. The result of this mechanism meant that any new modules, not specifically in the core or extra list files, produced during the build would end up in the kernel-uek-modules
RPM by default. Over time, this approach has presented problems with a growing modules list in the kernel-uek-modules
RPM. This is one of the reasons that we needed to create newer sub-packages (like kernel-uek-modules-desktop
, kernel-uek-modules-usb
, so on).
The first step was to replace filter-modules.sh
, mod-denylist.sh
, and related shell scripting within the kernel-uek.spec
file with a single Python script that can do everything required in one pass and more efficiently.
The format of the module lists changed from a file with one path per line to a pre-processed YAML file that fully specifies where to package every module. We will go into more detail on this next.
To handle modules that are only built in a specific architecture (x86_64, aarch64) or flavour (debug, 64k, etc.) the YAML file is pre-processed using cpp
, the C pre-processor, which helps keeps the module list compact and easier to maintain.
Previously, a maintainer had to deal with multiple list files – mod-extra.list
, modules-core-aarch64.list
and modules-core-x86_64.list
which is more error-prone than using one list. This would in turn become even more challenging on the introduction of new sub-packages in UEK8.
To summarise, some problems seen with the previous implementation:
-
We needed full paths in core list, while just the module name in extras
-
Without an explicit list of modules for the
kernel-uek-modules
RPM, it was the default for any new module coming from upstream or enabled by a kconfig change -
Some of the modules in the extras list are not actually packaged in
kernel-uek-modules-extra
, due to them being a dependency of a module in another RPM, resulting in inconsistencies in the module list, for example,atm.ko
is listed inmod-extra.list
but is packaged inkernel-uek-modules
.
The new packaging mechanism solves these problems.
Now let us take a look at the basic structure of the modules.yaml.S
file:
modules-core: - a - b /* modules-core: */ #ifdef Arch_aarch64 - c - d #endif /* Arch_aarch64 */ /* modules-core: */ #ifdef Arch_x86_64 - e - f #endif /* Arch_x86_64 */
Using the above example the organization of kernel modules would be as follows:
-
a
,b
will be inkernel-uek-modules-core
for both the architectures -
c
,d
will be inkernel-uek-modules-core
, but only for aarch64 architectures -
e
,f
will be inkernel-uek-modules-core
, but only for x86_64 architectures
Denylist changes
On UEK8, we have fixed these problems with module denylists (previously called “blacklists”):
-
Ideally, we should be able to blacklist any module from any package during the build process. However, currently, only modules listed in
mod-extra.list
are blacklisted. -
This above limitation can become problematic when combined with the issue where a module listed in
mod-extra.list
can be packaged inkernel-uek-modules.rpm
instead ofkernel-uek-modules-extra.rpm
, a blacklisted module can be installed and used even ifkernel-uek-modules-extra.rpm
(which contains the/etc/modname-blacklist.conf
file) is not installed, butkernel-uek-modules.rpm
is installed. -
Modifying denylist files was challenging because you might run into conflicts as installing another version will also have the exact same path.
To solve the first two issues we have added a new denylist.S
YAML file, which generates files for distribution to /etc/modprobe.d/
that will prevent a module from being automatically loaded.
The denylist.S
file can include modules from any sub-package, and the corresponding denylist is packaged together with the sub-package that contains the module. For instance, if the module_uek
is denylisted and is part of the kernel-uek-modules-desktop
sub-package, then the denylist corresponding to this module will also be included in that same sub-package.
For the last issue, we modified the denylist paths to include the KERNEL_VERSION string.
Stricter Depmod Checking
Depmod checking, using the depmod
tool is used to detect a module’s dependencies required for its run-time. More about depmod can be found in the depmod manual page [2].
For example:
depmod -b MODULES_DIR -aeF ./System.map $KernelVer &> depmod.out
During a build, while packaging, this test ensures that modules in a sub-package are usable without installing additional packages. So to avoid similar problems to the end-user, we do depmod
checking during the build stage.
A depmod
warning might look like:
depmod: WARNING: /lib/modules/KERNEL_VERSION/kernel/drivers/media/rc/keymaps/rc-siemens-gigaset-rc20.ko needs unknown symbol rc_map_register
This indicates that rc-siemens-gigaset-rc20.ko
module doesn’t know about rc_map_register()
, because the module exporting this symbol is not installed at that point.
Depmod Checking Pseudo Code
The build process begins with all modules installed, and by removing modules in a given sub-package, it is possible to validate that a reduced set of modules would have all the required dependencies.
all packages = (modules-core, modules, modules-desktop, modules-usb, modules-wireless, modules-extra-netfilter, modules-extra, modules-deprecated) new sub-packages = ( modules-desktop, modules-usb, modules-wireless, modules-extra-netfilter )
- Stage 1: Remove modules-extra and modules-deprecated – run
depmod
check withcurrent_list = (modules-core, modules, modules-desktop, modules-usb, modules-wireless, modules-extra-netfilter)
- Stage 2: Remove all new sub-packages and install only one package at a time – run depmod check
- Remove all new sub-packages – install only desktop → run
depmod
check withcurrent_list = (modules-core, modules, modules-desktop)
- Remove all new sub-packages – install only usb → run
depmod
check withcurrent_list = (modules-core, modules, modules-usb)
- Remove all new sub-packages – install only wireless → run
depmod
check withcurrent_list = (modules-core, modules, modules-wireless)
- Remove all new sub-packages – install only extra-netfilter → run
depmod
check withcurrent_list = (modules-core, modules, modules-extra-netfilter)
- Remove all new sub-packages – install only desktop → run
- Stage 3: Remove modules – run
depmod
check withcurrent_list = (modules-core)
Controlled placement of modules
The new filter-modules.py
script may generate errors about two important things during a build (if any):
- A list of any modules that are not built, but listed in the
modules.yaml.S
file. - A list of modules built, but are not packaged into any sub-package(not listed in the `modules.yaml.S file).
Sample build errors look like:
# Module was listed in `modules.yaml.S` file but not built error: Module hid-logitech-hidpp was not built? # Module was built but not sub-packages into any RPM error: lib/modules/KERNEL_VERSION/kernel/drivers/media/cec/i2c/tda9950.ko built but not specified by any sub-package
By making this strict at build time, a maintainer would have full control on placement of the module into its correct place. For example, if a laptop-related driver is now built after an update from LTS. Given that it is a laptop driver, a maintainer can place it explicitly in the kernel-uek-modules-desktop
package by editing the modules.yaml.S
file.
Packages dependency structure for UEK8 on OL9 and OL10
This section shows the details about the RPM dependencies on OL9 and OL10 for UEK8.
How to read this:
package-a: package-b
package-a
requires the installation of package-b
. This is directly mapped to Requires:
field in the RPM’s specifications.
Oracle Linux 9 sub-package requirements
kernel-uek: kernel-uek-core kernel-uek-modules-core kernel-uek-modules-desktop kernel-uek-modules kernel-uek-modules-extra-netfilter kernel-uek-modules-usb kernel-uek-modules-wireless kernel-uek-core: kernel-uek-modules-core kernel-uek-modules: kernel-uek-modules-core kernel-uek-modules-core: kernel-uek-core kernel-uek-modules-deprecated: kernel-uek-modules kernel-uek-modules-desktop: kernel-uek-modules kernel-uek-modules-extra: kernel-uek-modules kernel-uek-modules-extra-netfilter: kernel-uek-modules kernel-uek-modules-usb: kernel-uek-modules kernel-uek-modules-wireless: kernel-uek-modules
On Oracle Linux 9, the kernel-uek
package requires all of the newer sub-packages, this helps to prevent potential regressions for customers who are upgrading from UEK7 to UEK8, ensuring a smooth upgrade path, but the user is free to remove the unused sub-packages as described in “Sample script to harden the system” section in this blog [1].
Oracle Linux 10 sub-package requirements
kernel-uek: kernel-uek-core kernel-uek-modules-core kernel-uek-modules kernel-uek-core: kernel-uek-modules-core kernel-uek-modules: kernel-uek-modules-core kernel-uek kernel-uek-modules-core: kernel-uek-core kernel-uek-modules-deprecated: kernel-uek-modules kernel-uek-modules-desktop: kernel-uek-modules kernel-uek-modules-usb kernel-uek-modules-extra: kernel-uek-modules kernel-uek-modules-extra-netfilter: kernel-uek-modules kernel-uek-modules-usb: kernel-uek-modules kernel-uek-modules-wireless: kernel-uek-modules
A difference on Oracle Linux 10 is that all newer sub-packages are not explicit requirements for kernel-uek
because an upgrade from OL9 using Leapp is able to determine the set of required packages from the running system, and can in turn handle the installation of sub-packages.
The only other change is that the kernel-uek-modules-usb
sub-package is a requirement of the kernel-uek-modules-desktop
sub-package, since USB modules are commonly used on desktop systems.
Conclusion
In summary, this blog has explored the internals of packaging these kernel modules, providing some insights for UEK developers to work easily with packaging.