Installing Linux Kernels: generic, low-latency or PREEMPT_RT

3 minute read

Motivation: Achieving Real-Time (RT) capability

To build time-critical programs that requires a high-level of responsiveness on Linux, the most important step is to select and use the correct Linux kernel. This is the goal of this note.

The Linux kernel is frequently built and released. The latest version of the generic kernel build as I write this note is 5.9-rc3. Howerver, this kernel is not suitable for RT applications: Its scheduler optimizes for total throughput and thus prevent any single process from being the absolute higest priority. We will need either a Fully Preemptible Kernel or at least a low-latency one.

Obtaining generic and low-latency kernels

Fully-Preemptible kernels are only used for time-critical applications such as device and robot control. If the highest level of responsiveness is not required, one can look at mainline generic or low-latency kernels at the kernel archive. The difference between the kernel variants–generic, low-latency and fully preemptible–are explained here and here.

Both generic and low-latency kernels are frequently built and released on the kernel archieve. First, find your architecture by running arch. Note that x86_64 and amd64 are the same architectures. Second, install pre-compiled kernels. We download the required .deb files. For example to install the generic 5.8.0 kernel, download the following files:

linux-headers-5.8.0-050800_5.8.0-050800.202008022230_all.deb
linux-headers-5.8.0-050800-generic_5.8.0-050800.202008022230_amd64.deb
linux-image-unsigned-5.8.0-050800-generic_5.8.0-050800.202008022230_amd64.deb
linux-modules-5.8.0-050800-generic_5.8.0-050800.202008022230_amd64.deb

Install these debian files with dpkg. The new kernel is now installed.

Compiling a Fully Preemptible Linux Kernel

Fully-Preemptible Linux kernels are not available as precompiled binaries; we need to compile from source.

First, choose a RT patch from this mirror. I chose version 4.14.78-rt47 when writing this note. The lastest patch as of the time of this writing is 4.19-rt3, unfornately does not work on my machine running Ubuntu 16.04.

Note the kernel version you have choosen, download the corresponding kernel source code from this mirror. Both steps can be done via the terminal as below.

cd ~/Downloads
# download patch
wget https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/4.14/patch-4.14.78-rt47.patch.xz
# download kernel
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.14.78.tar.xz

Unzipped the archives and patch the kernel with the RT patch.

tar xvf linux-4.14.78.tar.xz
cd linux-4.14.78
xzcat ../patch-4.14.78-rt47.patch.xz | patch -p1 --verbose

We now enable the option Fully Preemptible Kernel (RT). First, install two necessary dependencies.

sudo apt-get install libncurses-dev libssl-dev

Configure the compilation by doing

make menuconfig

You will see a graphical user interface (GUI) show up in the terminal. This GUI contains configurations for compiling the Kernel. The bit that we need is called Preemption Model:

  • Navigate using arrow keys to Processor type and feature, then press Enter;
  • look for Preemption Model, press Enter;
  • choose Fully Preemptible Kernel (RT) option;
  • return to the first screen by pressing Esc multiple times;
  • save .config file, then exit.

Compile the Kernel and install.

make -j20
sudo make modules_install -j20
sudo make install -j20

Remark for 5.x version kernels

The Fully Preemptible Kernel (RT) option seems to have been shifted from Processor type and feature to General setup. Thanks Iain!

Configure the kernel

Now that the kernel is compiled and installed, we need to tell the bootloader about it. First, check that the new kernel can be found in /boot, then run sudo update-grub to generate new grub config file at /boot/grub/grub.cfg.

If you want to set a default kernel at boot or prevent the user from selecting a different kernel, you will need to configure the grub configuration file. Simple configuration is the easiest way to achieve this. Edit /etc/default/grub then rerun sudo update-grub to regenerate the configuration. To see documentation on the entries, use the command below for the documentation.

info -f grub -n 'Simple configuration'

Note that GRUB_DEFAULT specifies the default entries. If there are sub menuentry, then the value of GRUB_DEFAULT must have the form `x>y`, where x is the n-th main entry, and y is the n-th sub entry. This is my current default grub configuration.

GRUB_DEFAULT="1>2"
GRUB_TIMEOUT_STYLE="countdown"
GRUB_TIMEOUT="0"
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX=""

Try it out

And that’s it. Reboot, remember to choose the patched Kernel in Advanced Boot Setting, and enjoy! At this point uname -a should print out the kernel name.

References

Some useful reads I came across while working on this: