Skip to main content

Testing Preempt RT on the i.MX8MM SoC in 15 minutes

preempt-rt-logo.png

Looking at the kernel sources for NXP's i.MX8M SoCs, I noticed branch names like "lf-5.10.72-rt-2.2.0" and I wondered if NXP finally was going to offer RT preempt support within their BSP. Some other vendors have started supporting the patch set already a while ago but for NXP this is new.

In this post we are going to run this real-time enabled kernel on the i.MX8M-Mini evaluation board from NXP to get a first hand impression of what is required on the kernel level. We will not use the Yocto build system as this is only a wrapper around the distribution agnostic kernel development.

Getting the source

NXP publishes its Linux kernel tree through a git repository on the CodeAurora site backed by the Linux Foundation. For efficiency I just add this repo as a remote repository to my Linux git repository:

dzu@krikkit:/opt/src/git/linux (master)$ git remote add qoriq-components https://source.codeaurora.org/external/qoriq/qoriq-components/linux
dzu@krikkit:/opt/src/git/linux (master)$ git remote update

With this remote repository in place, we can access the tags and branches from it. The tags we are interested in originate in the "Linux Factory" group inside NXP. The idea is that this group works on the Linux support for all of the NXP products. This merges the previously separate releases for e.g. QorIQ products and i.MX products:

dzu@krikkit:/opt/src/git/linux (master)$ git tag -l | grep lf-
lf-5.10.35-2.0.0
lf-5.10.35-rt-2.0.0
lf-5.10.52-2.1.0
lf-5.10.52-rt-2.1.0
lf-5.10.72-2.2.0
lf-5.10.72-rt-2.2.0
lf-5.10.y-1.0.0
lf-5.10.y-1.0.0-rt
lf-5.4.47-1.1.0
lf-5.4.y-1.0.0
dzu@krikkit:/opt/src/git/linux (master)$

Just like most of the SoC vendors nowadays, NXP targets the Long Term Support (LTS) Linux kernels. You can find up to date information on these kernels through the active kernel releases page on kernel.org.

Although at the time of writing (February 2022) the latest upstream LTS kernel is 5.15, NXP does not yet offer this version through the Linux Factory kernels, so we will use 5.10 for this exercise which will be supported until end of 2026. The "-rt" tags hint that starting with this version, NXP provides branches pre-patched with the corresponding Preempt RT patchset. So let's checkout the most recent tag into its own branch:

dzu@krikkit:/opt/src/git/linux (master)$ git checkout -b branch-lf-5.10.72-rt-2.2.0 lf-5.10.72-rt-2.2.0
Updating files: 100% (41134/41134), done.
Switched to a new branch 'branch-lf-5.10.72-rt-2.2.0'
dzu@krikkit:/opt/src/git/linux (branch-lf-5.10.72-rt-2.2.0)$ 

Cross toolchain

To cross compile the kernel for the ARMv8 target, we need a suitable gcc cross compiler. There are different solutions to this but for the sake of simplicity, we will just use the cross compiler provided directly by Debian. Just install the gcc-aarch64-linux-gnu package and you are good to go. If you want to use another toolchain, i.e. the one produced by Yocto, this should of course also work just the same.

With this in place, we use the NXP provided i.MX kernel configuration as a starting point. In addition to patching the kernel, we also need to configure the kernel to be "fully pre-emptive" as "real-time" also has its drawbacks. By allowing the kernel to be fully pre-emptive, it will actually loose some performance for non real-time workloads. This is one of the reasons why the patch set is still not completely in the mainline Linux kernel.

So in order to enable CONFIG_PREEMPT_RT we enter the configuration GUI:

dzu@krikkit:/opt/src/git/linux (branch-lf-5.10.72-rt-2.2.0)$ export ARCH=arm64
dzu@krikkit:/opt/src/git/linux (branch-lf-5.10.72-rt-2.2.0)$ export CROSS_COMPILE=aarch64-linux-gnu-
dzu@krikkit:/opt/src/git/linux (branch-lf-5.10.72-rt-2.2.0)$ make imx_v8_defconfig
  HOSTCC  scripts/kconfig/conf.o
  HOSTCC  scripts/kconfig/confdata.o
  HOSTCC  scripts/kconfig/expr.o
  LEX     scripts/kconfig/lexer.lex.c
  YACC    scripts/kconfig/parser.tab.[ch]
  HOSTCC  scripts/kconfig/lexer.lex.o
  HOSTCC  scripts/kconfig/parser.tab.o
  HOSTCC  scripts/kconfig/preprocess.o
  HOSTCC  scripts/kconfig/symbol.o
  HOSTCC  scripts/kconfig/util.o
  HOSTLD  scripts/kconfig/conf
#
# configuration written to .config
#
dzu@krikkit:/opt/src/git/linux (branch-lf-5.10.72-rt-2.2.0)$ make menuconfig

In the GUI we head straight to "General Setup" and "Preemption Model". To our surprise we see just the "regular" options of non-RT enabled kernels:

preempt-rt-config1.png

So with our previous knowledge we know that we are looking for the PREEMPT_RT option (usually we leave off the "CONFIG_" prefix to save some space), but it is not shown in the interface. So even though the kernel itself is patched with the RT preempt patch set, we cannot enable the relevant kernel configuration. This tells us that the NXP team likely has not yet finalized the RT preempt support and does not yet provide a ready to use configuration. Considering that this is the first LTS version where the patch set is already included in the NXP branch this is kind of understandable. Things take time and I expect the situation to change in the next versions.

But for now we have to find out ourselves why we cannot select the option present in the sources. We could start by looking at all the relevant Kconfig files but this is a manual process and by looking at the sources we will only see an "offline" view of the data structures. For conditional constructs you have to mentally parse them and substitute the actual values for a given .config file to work out the actual truth values. It is much better to have a "live" view of the option set together with showing the conditions of options. Fortunately menuconfig can do that for us. It is contained within its "search" functionality.

So in order to make progress in our quest for a real time enabled kernel we search for the CONFIG_PREEMPT_RT symbol itself. Just type "/" followed by "PREEMPT_RT":

preempt-rt-config3.png

The "Depends on:" line tells us the option is hidden because of EXPERT currently being disabled and because of ARCH_SUPPORTS_RT also being false (n). EXPERT is an option in the same hierarchy and can easily be activated. So let's look at what is the matter with ARCH_SUPPORTS_RT:

preempt-rt-config4.png

ARM64 is enabled, so we need to dig down further on HAVE_POSIX_CPU_TIMERS_TASK_WORK:

preempt-rt-config5.png

Aha! So in this kernel this option is incompatible with the Kernel Virtual Machine (KVM) virtualization support. I would be surprised if this was a permanent situation but for Linux 5.10 on ARM64 we can either have KVM or RT preempt but not both.

So let's test our hypothesis by enabling EXPERT and disabling KVM. You can easily do this with the GUI but here is a quick hack to do it from the command line. Just amend .config with the wanted choices and on saving a configuration the next time from within menuconfig the config file will again be in a canonical format. You will get warnings on the console however when you do this - but they have not been captured in the screenshots here.

dzu@krikkit:/opt/src/git/linux (branch-lf-5.10.72-rt-2.2.0)$ make imx_v8_defconfig
#
# configuration written to .config
#
dzu@krikkit:/opt/src/git/linux (branch-lf-5.10.72-rt-2.2.0)$ echo -e "CONFIG_EXPERT=y\nCONFIG_KVM=n" >> .config
dzu@krikkit:/opt/src/git/linux (branch-lf-5.10.72-rt-2.2.0)$ make menuconfig

preempt-rt-config2.png

Finally, we can now select PREEMPT_RT, save the configuration and build the kernel.

dzu@krikkit:/opt/src/git/linux (branch-lf-5.10.72-rt-2.2.0)$ make -j $(nproc) Image
arch/arm64/Makefile:33: LSE atomics not supported by binutils
arch/arm64/Makefile:44: Detected assembler with broken .inst; disassembly will be unreliable
  SYNC    include/config/auto.conf.cmd
  CC      scripts/mod/empty.o
  MKELF   scripts/mod/elfconfig.h
  HOSTCC  scripts/mod/modpost.o
  CC      scripts/mod/devicetable-offsets.s
  HOSTCC  scripts/mod/file2alias.o
  HOSTCC  scripts/mod/sumversion.o
  HOSTLD  scripts/mod/modpost
  CC      kernel/bounds.s
[...]
  AR      drivers/mxc/built-in.a
  AR      drivers/built-in.a
  GEN     .version
  CHK     include/generated/compile.h
  UPD     include/generated/compile.h
  CC      init/version.o
  AR      init/built-in.a
  LD      vmlinux.o
  MODPOST vmlinux.symvers
  MODINFO modules.builtin.modinfo
  GEN     modules.builtin
  LD      .tmp_vmlinux.kallsyms1
  KSYMS   .tmp_vmlinux.kallsyms1.S
  AS      .tmp_vmlinux.kallsyms1.S
  LD      .tmp_vmlinux.kallsyms2
  KSYMS   .tmp_vmlinux.kallsyms2.S
  AS      .tmp_vmlinux.kallsyms2.S
  LD      vmlinux
  SORTTAB vmlinux
  SYSMAP  System.map
  OBJCOPY arch/arm64/boot/Image
dzu@krikkit:/opt/src/git/linux (branch-lf-5.10.72-rt-2.2.0)$

Running the kernel

To quickly test the kernel, I copied Image and imx8mm-evk.dtb into the directory served by my tftp server. As any root filesystem will work, I will use the Yocto image already contained on the SD card and only load the kernel and the dtb from my tftp server. To do this, I stop U-Boot, define a new command, set up the IP configuration for the tftp server and execute it:

u-boot=> setenv net_mmc 'tftp $fdt_addr $fdtfile; tftp $loadaddr $image; run mmcargs; booti $loadaddr - $fdt_addr'
u-boot=> setenv serverip 192.168.44.35
u-boot=> setenv ipaddr 192.168.44.119
u-boot=> saveenv
Saving Environment to MMC... Writing to MMC(1)... OK
u-boot=> run net_mmc
Using ethernet@30be0000 device
TFTP from server 192.168.44.35; our IP address is 192.168.44.119
Filename 'imx8mm-evk.dtb'.
Load address: 0x43000000
Loading: ####
         751 KiB/s
done
Bytes transferred = 46942 (b75e hex)
Using ethernet@30be0000 device
TFTP from server 192.168.44.35; our IP address is 192.168.44.119
Filename 'Image'.
Load address: 0x40480000
Loading: #################################################################
         #################################################################
         #################################################################
         #################################################################

[...]

NXP i.MX Release Distro 5.10-hardknott imx8mmevk ttymxc1

imx8mmevk login: root
[   25.385526] audit: type=1006 audit(1645537297.109:4): pid=953 uid=0 old-auid=4294967295 auid=0 tty=(none) old-ses=4294967295 ses=3 res=1
root@imx8mmevk:~# uname -a
Linux imx8mmevk 5.10.72-rt53-00346-g3e5f5dc7a6de #6 SMP PREEMPT_RT Sun Jan 30 15:52:04 CET 2022 aarch64 aarch64 aarch64 GNU/Linux
root@imx8mmevk:~# 

Conclusion

So indeed it is now possible to get an RT preempt kernel running on the i.MX8M-Mini in a minimum of time. But currently this still requires some deeper knowledge of how things work on the kernel level. As this is the first Linux LTS version with such support from NXP I expect the situation change for the better in the future. Currently I also do not see any Yocto recipe to compile such a kernel and I am sure that this will also be added sooner or later.

In order to find out if the kernel not only boots but also improves the latency behavior, we will have to run some test, but this will be the content of another post. So stay tuned!

Update 2022-04-26

Through the comment of XQ Zhou I was made aware that I mistakenly attributed the "lf-5.10.72-rt-2.2.0" tag to the imx/linux-imx repository on CodeAurora instead of the qoriq-components/linux repository it seems to originate from. Of course the whole idea of the "Linux Factory" is to unite those repositories but this is not quite true yet.

Comments

Comments powered by Disqus