Hand-building an updated upstream kernel module for your (Fedora) kernel
Suppose, not hypothetically, that you've run into an issue where a distribution kernel update has a new bug in some functionality that's important to you, for instance Fedora's 4.20.x kernels fail to see one fan sensor on your Asus Prime Z370-A motherboard. After some work, you identify the issue, report it, and the fix is made upstream. Now you would like to test this upstream fix in your kernel.
The proper way to do this would be to extract some form of the
change that fixes things as a patch, get the source RPM for the
latest Fedora kernel, add your patch to the specfile as another RPM
patch, rebuild your new kernel source RPM to get the binary kernel
RPMs, and install them (let's assume you don't have to worry about
signed kernels). However, this is a lot of work and I'm lazy, so I
took the easier way of rebuilding this changed code as an out of
kernel module and then just dumping it into
/lib/modules as a
replacement for the regular Fedora kernel module from the kernel
RPM. And rather than trying to create the proper control files for
this, we'll just see if we can just build from the actual kernel
source directory involved, which it turns out that we can in this
case (but perhaps not always).
(As a disclaimer, my process here probably only works under the right circumstances because it's a hack.)
That's confusing, so let me put it more simply: we have some kernel source tree, let's call it groeck/linux-staging, and we're going to build a module from that source tree but build it for our currently running Fedora kernel. This tree has the fix we want, but it's not actually a kernel tree for the right version of the kernel, which is part of what makes this a hack.
(The current Fedora kernel is based on 4.20.5, while groeck/linux-staging is currently based on 5.0-rc3, because it's fixes that are intended to be pulled into the main Linux kernel. Eventually these fixes will hopefully make their way back into the 4.20.x series and all of this will be unnecessary.)
In theory, building a kernel module out of the tree once you have
Makefile in the directory is very easy.
The kbuild modules.txt
documentation describes the important magic. When you're in the
directory where the source of the module you want to build is, you
make -C /lib/modules/$(uname -r)/build M=$(pwd)
If all goes well, this will build your module to a <whatever>.ko
nct6775.ko. You probably want to compress this with
xz, because that turns out to be the standard for kernel modules
nowadays. You can check that things look good with '
nct6775.ko.xz'. My actual process wound up somewhat more involved
than this, partly because I'm a cautious person and partly because
I tried some things that exploded.
- Clone the Git repo with the fix
to somewhere, say
- Clone the stable kernel Git repo to
/var/tmp/linux-stable. This isn't absolutely required, but we're going to use it to make sure we're not picking up unexpected changes.
- In the stable repo, check out the branch for your particular kernel
line. Fedora is based on 4.20.x currently, so:
git checkout linux-4.20.y
- In the staging repo, switch to the appropriate branch. Don't
accidentally build from the wrong branch, giving you a version of
the module without the fix, the way I did the first time.
git checkout hwmon-next
You may have to hunt around with '
git branch -r' and so on to find out where your fix is hiding. If you know the git commit ID, this stackoverflow answer led me to '
git branch --contains <commit-id>', which would have saved me having to guess and be somewhat lucky.
- Compare the module source in the stable branch and in the
development version. There is probably a really clever way to put
both in the same Git repo and compare with a git command, but I
took the simple brute force way of two repos:
cd /var/tmp diff -u linux-stable/drivers/hwmon/nct6775.c linux-staging/drivers/hwmon/nct6775.c
You want to see little or no changes other than the bugfix for your issue. If there are significant changes, the module from the upstream may not compile for the stable kernel you want to build it against, or work properly in that kernel, and you have more work to do. I was lucky and the only change in
nct6775.cthat I cared about was the bugfix for my issue.
- Change into the directory holding the updated module you want and
explicitly tell the module build process what module to build,
instead of letting it build everything in the directory. Here
we want to build the
cd /var/tmp/linux-staging/drivers/hwmon make -C /lib/modules/$(uname -r)/build M=$(pwd) nct6775.ko
If you leave out the specific module you want to build, the build system will try to build all of them and you may find that some of the modules in your upstream tree aren't compatible with the stable kernel you're trying to build against. This is the case here and is why I wound up comparing the nct6775.c code in the stable tree against the upstream; even though it built against stable, I wanted some assurance that there hadn't been changes that would mean I was shooting myself in the foot.
(I may still be shooting myself in the foot, but at least it's not obvious.)
nct6775.kobuilt, you can compress it with
xz, then copy it to
/lib/modules/$(uname -r)/extra/, save the original module (located in
kernel/drivers/hwmon) to some other place, and run '
depmod $(uname -r)' to re-apply magic module processing. If you reboot, you should be using your new hand-built module and in my case, my case fan was back to being reported.
You can pre-build the module against a kernel other than what you're
currently running by replacing the '
$(uname -r)' bits with the
actual name of the other kernel. This is handy if you're installing
an updated kernel and don't want to reboot twice.
(Clever people who know what they're doing can proceed to make this a DKMS module. It's apparently not hard, but I'm in a brute force mood here.)