Quickly building a custom Linux (Ubuntu) kernel, with modified configuration (kernel timer frequency)
Recently, I had to build a custom Linux kernel with a modified configuration (specifically, a modified kernel timer frequency).
This is a relatively typical task, so there is plenty of information around, however, I’ve found lack of clarity about the concepts involved, outdated and incompleted information, etc.
For this reason, I’ve decided to write a small guide about this task, in the form of a truly-hassle-free-copy-paste™ set of commands, with some clarifications.
As per my blogging style, I’ve spiced the script up with some (Linux/Scripting/Regex)-fu - of course, for the lulz™.
Content:
- Requirements
- Procedure
- Conclusion
- References
Requirements
This guide is based on Ubuntu systems, however, it can be easily adapter to other systems, in particular, Debian-based ones.
The commands must be executed in a single terminal session, since at least one variable is used multiple times.
Aside the (optional) step where the user interactively configures the kernel, the commands can be pasted into a script that takes care of everything.
Procedure
Preparation
Let’s create an empty directory, and find out the running kernel version, so that we’ll compile the same:
mkdir linux-ubuntu
cd linux-ubuntu
kernel_release=$(uname -r)
Handling the ZFS module
If the system is running on ZFS, some care needs to be taken (otherwise, ignore this step).
First, we need to install the zfs-dkms
package (see StackOverflow question), in order to compile and add the module to the custom kernel.
Additionally, if running on kernel 5.8+ (e.g Ubuntu Focal HWE), then the ZFS module may not build, so we install version 5.4 (see Launchpad report).
# Check if the module is running.
#
if lsmod | grep -qP '^zfs\s'; then
sudo apt install zfs-dkms
# `uname -r` sample output:
#
# 5.8.0-38-generic
#
if dpkg --compare-versions "$(uname -r | awk -F. '{ print $1"."$2 }')" ge 5.8; then
# `apt-cache search` sample output:
#
# linux-image-unsigned-5.4.0-62-generic - Linux kernel image for version 5.4.0 on 64 bit x86 SMP
#
kernel_release=$(apt-cache search '^linux-image-unsigned-5.4.[[:digit:]-]+-generic' | perl -ne 'eof && print /unsigned-(\S+)/')
fi
fi
Installing the build dependencies and the kernel source packages
For simplicity, we’re going to download the Ubuntu-specific kernel source packages; they are stored in the source repositories, which are not enabled by default, so we enable them:
# Source lines to change:
#
# # deb-src http://de.archive.ubuntu.com/ubuntu/ focal main restricted
# # deb-src http://de.archive.ubuntu.com/ubuntu/ focal-updates main restricted
#
sudo perl -i.bak -pe "s/^# (deb-src .* $(lsb_release -cs)(-updates)? main restricted$)/\$1/" /etc/apt/sources.list
Install the build dependencies:
# Probably more packages than needed, but it's tedious (and fragile) to find out and install exactly
# the needed ones.
#
sudo apt install kernel-package libncurses5 libncurses5-dev libncurses-dev qtbase5-dev-tools flex \
bison openssl libssl-dev dkms libelf-dev libudev-dev libpci-dev libiberty-dev autoconf
sudo apt build-dep "linux-image-unsigned-$kernel_release"
and download/prepare the kernel source packages:
# We need to install the `unsigned` package, since the `linux-image-$kernel_release` package does not
# include the source.
#
apt source "linux-image-unsigned-$kernel_release"
Note that both apt build-dep
and apt source
require the source repositories to be enabled.
At this stage, if desired, we can disable the source repositories back:
sudo mv -f /etc/apt/sources.list{.bak,}
Alternative sources for the Linux kernel source
There are alternative sources for the kernel source:
git://kernel.ubuntu.com/ubuntu/ubuntu-$release_codename.git
: the Ubuntu custom kernelgit@github.com:torvalds/linux.git
: the vanilla kernel
although it’s more convenient to use the source packages.
Customizing the kernel configuration
Enter the kernel source directory:
cd */
Now we need to generate the configuration (.config
file), and optionally modify it.
Modifications to the kernel configuration should not be performed manually; even a single change like the kernel timer frequency is not a single line change.
Using the kernel source packages (downloaded in the previous section) has the advantage that it includes the changes used by the Ubuntu kernel(s).
Now we have a few options.
Create the default Ubuntu configuration
Run the following:
make oldconfig
Create the default Ubuntu configuration, and customize it
In order to create a default Ubuntu configuration, and customize it, run the following:
make xconfig
and edit away!
There is also a terminal tool for the same purpose:
make menuconfig
however, the GUI version is much more practical, since it shows the configuration tree and the subtree entries at the same time, making it much easier to explore it.
For those who want to change the kernel timer frequency, the entry is listed as Processor type and features
-> Timer frequency
.
Unnecessary components can be removed, which will save compile time, although it’s important to be 100% sure of it 🙂
Building the kernel
Time to build the kernel!
It’s good practice to add a version modifier, in order to make the kernel recognizable:
kernel_local_version_modified="timer-100"
fakeroot make-kpkg -j "$(nproc)" --initrd --append-to-version="$kernel_local_version_modified" kernel-image kernel-headers
This will take some time (several minutes), and generate two *.deb
packages in the parent directory.
Installing, rebooting and testing
Install the generated packages:
sudo dpkg -i ../*.deb
Now reboot; the new kernel will be in the GRUB list.
After booting, one can verify that the changes have been applied:
$ grep -P '^CONFIG_HZ=\d+$' "/boot/config-$(uname -r)"
CONFIG_HZ=100
Yay!
Conclusion
Although I would have expected the procedure to be trivial, it wasn’t. Once the involved concepts were clear though, the procedure became simple and straightforward.
It’s now trivially possible for everybody to have a standard-as-desired kernel, with the intended customizations.
Happy kernel hacking!