On Backporting Kernel Modules with DKMS
Recently, I bought a USB 3.0 2.5 Gbps Ethernet dongle for my Atomic Pi
router. This dongle requires a version of the r8152
kernel driver with
support for the RTL8156 chipset, which is only added in Linux 5.13. Now, I
am running the Debian stable kernel and have no wish to backport the latest
5.13 kernel simply for that one driver. So of course, I came up with an
approach to backport a driver from a newer version.
In this blog post, I will walk you through the process of backporting a
single kernel module, using the r8152
kernel driver as an example.
To start, we create a directory called r8152-backport
, where we will be
storing files related to this exercise, and fetch the latest version of
the kernel module:
$ mkdir r8152-backport
$ cd r8152-backport
$ wget https://raw.githubusercontent.com/torvalds/linux/master/drivers/net/usb/r8152.c
We can then create a simple Makefile
to build the kernel. Save the following
block as Makefile
, and remember to replace r8152
with your module name
if you are backporting something else:
obj-m += r8152.o
USER := $(shell whoami)
KVER ?= $(shell uname -r)
KDIR ?= /lib/modules/$(KVER)/build
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
.PHONY: test
We can then run make
to build this module:
$ make
make -C /lib/modules/5.10.0-8-amd64/build M=/home/quantum/build/r8152-backport modules
make[1]: Entering directory '/usr/src/linux-headers-5.10.0-8-amd64'
CC [M] /home/quantum/build/r8152-backport/r8152.o
/home/quantum/build/r8152-backport/r8152.c:29:10: fatal error: linux/usb/r8152.h: No such file or directory
29 | #include <linux/usb/r8152.h>
| ^~~~~~~~~~~~~~~~~~~
compilation terminated.
make[3]: *** [/usr/src/linux-headers-5.10.0-8-common/scripts/Makefile.build:284: /home/quantum/build/r8152-backport/r8152.o] Error 1
make[2]: *** [/usr/src/linux-headers-5.10.0-8-common/Makefile:1845: /home/quantum/build/r8152-backport] Error 2
make[1]: *** [/usr/src/linux-headers-5.10.0-8-common/Makefile:185: __sub-make] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-5.10.0-8-amd64'
make: *** [Makefile:7: all] Error 2
It appears that this module requires its own header file. Fortunately, this
is easily remedied. We simply have to download r8152.h
and make r8152.c
include our version. You can find the file by searching the kernel source
tree. This is one way to do it:
$ wget https://raw.githubusercontent.com/torvalds/linux/master/include/linux/usb/r8152.h
$ sed -i '/r8152\.h/c\#include "r8152.h"' r8152.c
Now this kernel module should build. Let’s see what happens:
$ make
make -C /lib/modules/5.10.0-8-amd64/build M=/home/quantum/build/r8152-backport modules
make[1]: Entering directory '/usr/src/linux-headers-5.10.0-8-amd64'
CC [M] /home/quantum/build/r8152-backport/r8152.o
/home/quantum/build/r8152-backport/r8152.c:8852:12: warning: ‘struct kernel_ethtool_coalesce’ declared inside parameter list will not be visible outside of this definition or declaration
8852 | struct kernel_ethtool_coalesce *kernel_coal,
| ^~~~~~~~~~~~~~~~~~~~~~~
/home/quantum/build/r8152-backport/r8152.c:8873:12: warning: ‘struct kernel_ethtool_coalesce’ declared inside parameter list will not be visible outside of this definition or declaration
8873 | struct kernel_ethtool_coalesce *kernel_coal,
| ^~~~~~~~~~~~~~~~~~~~~~~
/home/quantum/build/r8152-backport/r8152.c:9088:18: error: initialization of ‘int (*)(struct net_device *, struct ethtool_coalesce *)’ from incompatible pointer type ‘int (*)(struct net_device *, struct ethtool_coalesce *, struct kernel_ethtool_coalesce *, struct netlink_ext_ack *)’ [-Werror=incompatible-pointer-types]
9088 | .get_coalesce = rtl8152_get_coalesce,
| ^~~~~~~~~~~~~~~~~~~~
/home/quantum/build/r8152-backport/r8152.c:9088:18: note: (near initialization for ‘ops.get_coalesce’)
/home/quantum/build/r8152-backport/r8152.c:9089:18: error: initialization of ‘int (*)(struct net_device *, struct ethtool_coalesce *)’ from incompatible pointer type ‘int (*)(struct net_device *, struct ethtool_coalesce *, struct kernel_ethtool_coalesce *, struct netlink_ext_ack *)’ [-Werror=incompatible-pointer-types]
9089 | .set_coalesce = rtl8152_set_coalesce,
| ^~~~~~~~~~~~~~~~~~~~
/home/quantum/build/r8152-backport/r8152.c:9089:18: note: (near initialization for ‘ops.set_coalesce’)
/home/quantum/build/r8152-backport/r8152.c:9197:3: error: ‘const struct net_device_ops’ has no member named ‘ndo_eth_ioctl’; did you mean ‘ndo_do_ioctl’?
9197 | .ndo_eth_ioctl = rtl8152_ioctl,
| ^~~~~~~~~~~~~
| ndo_do_ioctl
/home/quantum/build/r8152-backport/r8152.c:9197:20: error: initialization of ‘netdev_tx_t (*)(struct sk_buff *, struct net_device *)’ {aka ‘enum netdev_tx (*)(struct sk_buff *, struct net_device *)’} from incompatible pointer type ‘int (*)(struct net_device *, struct ifreq *, int)’ [-Werror=incompatible-pointer-types]
9197 | .ndo_eth_ioctl = rtl8152_ioctl,
| ^~~~~~~~~~~~~
/home/quantum/build/r8152-backport/r8152.c:9197:20: note: (near initialization for ‘rtl8152_netdev_ops.ndo_start_xmit’)
cc1: some warnings being treated as errors
make[3]: *** [/usr/src/linux-headers-5.10.0-8-common/scripts/Makefile.build:284: /home/quantum/build/r8152-backport/r8152.o] Error 1
make[2]: *** [/usr/src/linux-headers-5.10.0-8-common/Makefile:1845: /home/quantum/build/r8152-backport] Error 2
make[1]: *** [/usr/src/linux-headers-5.10.0-8-common/Makefile:185: __sub-make] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-5.10.0-8-amd64'
make: *** [Makefile:7: all] Error 2
Oh no! It appears that the kernel interface has changed in the meantime.
We have two options here: either use an older version of this module before
this change, or we can attempt to revert it. Either way, we’ll need to find
when this change happened. Doing a quick git blame
shows that the
changes were introduced in f3ccfda19319
and a76053707dbf
. We can
use the code from the commit before the earlier of the two, which in this case
is a554bf96b49d
, the parent commit of a76053707dbf
. However, I
am just going to revert the two changes:
$ curl -sSL https://github.com/torvalds/linux/commit/f3ccfda1931977b80267ba54070a1aeafa18f6ca.diff | sed -n '/^diff.*r8152\.c/,/^diff/{/^diff/d;p}' | patch -R r8152.c
patching file r8152.c
$ curl -sSL https://github.com/torvalds/linux/commit/a76053707dbf0dc020a73b4d90cd952409ef3691.diff | sed -n '/^diff.*r8152\.c/,/^diff/{/^diff/d;p}' | patch -R r8152.c
patching file r8152.c
Hunk #1 succeeded at 9190 (offset 17 lines).
Remember, we are only interested in changes to r8152.c
, so we use sed
to filter out those changes.
Now, the kernel module should build:
$ make
make -C /lib/modules/5.10.0-8-amd64/build M=/home/quantum/build/r8152-backport modules
make[1]: Entering directory '/usr/src/linux-headers-5.10.0-8-amd64'
CC [M] /home/quantum/build/r8152-backport/r8152.o
MODPOST /home/quantum/build/r8152-backport/Module.symvers
CC [M] /home/quantum/build/r8152-backport/r8152.mod.o
LD [M] /home/quantum/build/r8152-backport/r8152.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.10.0-8-amd64'
We can now sudo insmod r8152.ko
to load our version of the module, but it’s
probably better to use dkms
to manage this module. To do this, we first need
to create dkms.conf
:
PACKAGE_NAME="r8152"
PACKAGE_VERSION="5.15.0"
MAKE[0]="make -C $kernel_source_dir M=$dkms_tree/$module/$module_version/build modules"
CLEAN="rm src/r8152.ko src/*.o || true"
BUILT_MODULE_NAME[0]="r8152"
DEST_MODULE_LOCATION[0]="/updates"
AUTOINSTALL="yes"
If you are backporting another module, you probably want to change r8152
to
the name of your module. For PACKAGE_VERSION
, I set it to the kernel version
I sourced the module from.
We can then install the module with dkms
:
$ sudo dkms install .
Creating symlink /var/lib/dkms/r8152/5.15.0/source ->
/usr/src/r8152-5.15.0
DKMS: add completed.
Kernel preparation unnecessary for this kernel. Skipping...
Building module:
cleaning build area...
make -j24 KERNELRELEASE=5.10.0-8-amd64 -C /lib/modules/5.10.0-8-amd64/build M=/var/lib/dkms/r8152/5.15.0/build modules...
cleaning build area...
DKMS: build completed.
r8152.ko:
Running module version sanity check.
- Original module
- Installation
- Installing to /lib/modules/5.10.0-8-amd64/updates/dkms/
depmod...
DKMS: install completed.
If you ever need to uninstall the module, simply run:
$ sudo dkms uninstall r8152/5.15.0 --all