From Fedora Project Wiki


Packaging kernel modules for Fedora Extras

Introduction

Kernel modules are a special case in rpm packaging and need careful handling. There are a lot of ways to package kernel modules -- to avoid confusion for the users and reviewers as well as to make it easier for RPM depsolvers to support kernel-modules the Fedora Extras Steering Commitee (FESCo) worked out below standard for packaging kernel-modules in Fedora. This standard hopefully solves many pitfalls earlier standards in other repos had.

The most important rule for packages with kernel modules: There are always at least two SRPMS -- one builds a userland package of the source, the other builds packages with *only* the kernel-module(s) in it.

Note
The kernel-module packaging standard is quite new -- there still might be some issues that need finetuning, so be prepared for a little more maintain work. This document also still needs a bit love.
Note
Fedora Extras has some special polices for packaging and reviewing kernel-module packages for Extras. They can be found in the Extras/Policy/KernelModules . Be sure to read them, too -- this document only describes the technical issues around packaging modules.

userland package

The binary packages build from the userland SRPM contains tools, documentation, license, udev configuration etc. There always has to exist such a package -- even if the packaged software only builds kernel-modules it has at least some docs and a license file that need to be packaged in the userland package.

The packager is free to split the userland-package further into those with a the general userland parts, that works fine without the kernel-modules (docs, general tools, devel-files), and one with the kernel-module related parts (udev rules for example).

The userland packages follow the usual Packaging Guidelines. Two additional rules MUST be followed for packages with parts related to the kernel-module(s):

  1. The package must tie itself to the kernel-module using something like 'Requires: %{name}-kmod >= %{version}'
  2. The package must provide %{name}-kmod-common using something like 'Provides: %{name}- kmod-common = %{version}' or the name of the package must be %{name}-kmod-common

kernel-module package

Of course all general rules that apply to rpm packaging in Fedora Extras apply for kernel module packages, too. Especially those around the licensing -- there are a lot of kernel-modules out there that can't be included in Fedora Extras due to licensing issues.

Besides the normal rules there are several additional rules -- instead of writing all those down FESCo created a specfile template and a script (used by the specfile) that handles most things automatically. Both are described in detail below.

All kernel module packages should use the template (see below) as a base. Reviewers of kernel modules should diff the proposed kernel module packages against the template. Only the names and the way the modules itself are build should differ. There shouldn't be other differences without a good reason.

Script

The bash script that is used by kernel-module-packages is named kmodtool. It is planed to be added to the redhat-rpm-config package and to be available to with the macro %{kmodtool}. In the testing phase the script is part of the packages. The current up2date version can be found here .

The script is no real magic -- if you understand a bit of bash scripting you should be able to see how it works. The most important part is the function get_rpmtemplate and this part of it:

%package       -n kmod-${kmod_name}${dashvariant}
Summary:          ${kmod_name} kernel module(s)
Group:            System Environment/Kernel
Provides:         kernel-modules = ${verrel}${variant}
Provides:         ${kmod_name}-kmod = %{?epoch:%{epoch}:}%{version}-%{release}
Requires:         kernel-%{_target_cpu} = ${verrel}${variant}
Requires:         ${kmod_name}-kmod-common >= %{?epoch:%{epoch}:}%{version}
Requires(post):   /sbin/depmod
Requires(postun): /sbin/depmod
BuildRequires:    kernel${dashvariant}-devel-%{_target_cpu} = ${verrel}
%description   -n kmod-${kmod_name}${dashvariant}
This package provides the ${kmod_name} kernel modules built for the Linux
kernel ${verrel}${variant} for the %{_target_cpu} family of processors.
%post          -n kmod-${kmod_name}${dashvariant}
/sbin/depmod -aeF /boot/System.map-${verrel}${variant} ${verrel}${variant} > /dev/null || :
%postun        -n kmod-${kmod_name}${dashvariant}
/sbin/depmod -aF /boot/System.map-${verrel}${variant} ${verrel}${variant} &> /dev/null || :
%files         -n kmod-${kmod_name}${dashvariant}
%defattr(644,root,root,755)
/lib/modules/${verrel}${variant}/extra/${kmod_name}/

This macro later expands to something like the following and is inserted into the spec file before building (with kmod_name foo, Version 1.5, kver 2.6.14-1.1776_FC4, uniprocessor Build for i686):

%package       -n kmod-foo
Summary:          foo kernel module(s)
Group:            System Environment/Kernel
Provides:         kernel-modules = 2.6.21-1.1776_FC7
Provides:         foo-kmod = 1.5-1
Requires:         kernel-i686 = 2.6.21-1.1776_FC7
Requires:         foo-kmod-common >= 1.5
Requires(post):   /sbin/depmod
Requires(postun): /sbin/depmod
BuildRequires:    kernel-devel-i686 = 2.6.21-1.1776_FC7
%description   -n kmod-foo
This package provides the foo kernel modules built for the Linux
kernel 2.6.21-1.1776_FC7 for the i686 family of processors.
%post          -n kmod-foo
/sbin/depmod -aeF /boot/System.map-2.6.21-1.1776_FC7 2.6.21-1.1776_FC7 > /dev/null || :
%postun        -n kmod-foo
/sbin/depmod -aF /boot/System.map-2.6.21-1.1776_FC7 2.6.21-1.1776_FC7 &> /dev/null || :
%files         -n kmod-foo
%defattr(644,root,root,755)
/lib/modules/2.6.21-1.1776_FC7/extra/foo/

Why all that? Let's go though the interesting bits in detail:

  • %package -n kmod-foo

All kernel modules need to have the prefix kmod (that's a bit shorter than kernel-module)

  • Provides: kernel-modules = 2.6.21-1.1776_FC7

With the explicit Provides depsolvers such as yum can know that it's working with a kernel module. You also easily can get the kver the module was build for with this.

  • Provides: foo-kmod = 1.5

The userland-package that depends on a package that provides that to make sure that yum and other depsolvers install a proper kernel-module if you install a userland package that requires a kernel-module.

  • Requires: kernel-i686 = 2.6.21-1.1776_FC7

A kernel module without the kernel it was built for is useless. Don't use /boot/vmlinuz-*, it's not portable.

  • Requires: foo = 1.5

Kernel modules without the userland part is useless in most cases. There are rare packages when kernel modules don't need a part in userland, but we require it anyway -- at least the license and the docs needs to be placed somewhere in any case and a userland package is the right place for them.

  • BuildRequires: kernel-devel-i686 = 2.6.21-1.1776_FC7

Needed for building kernel-modules

  • %defattr(644,root,root,755)

Kernel modules shall not be executable -- but they need to be after %install to allow /usr/lib/rpm/find-debuginfo.sh to strip them.

  • /lib/modules/2.6.21-1.1776_FC7/extra/foo/

Separate location -- don't mess up with the rest of the kernel. "extra" was picked because of upstream kernel documentation. Only kernel modules in that dir are allowed -- nothing else, because otherwise they might conflict between different versions!

kernel module specfile

An example kernel module specfile for "foo" might looks like this (download File:Obsolete KernelModules kmod-template.spec ; note that the first part of the page needs adjustments for the different releases -- see the end of this page for up2date examples):

Source10: kmodtool
%define   kmodtool bash %{SOURCE10}

%{!?kversion: %define kversion 2.6.21-1.2111_FC7}

%define kmod_name foo
%define kverrel %(%{kmodtool} verrel %{?kversion} 2>/dev/null)

%define upvar ""
%ifarch i586 i686 ppc
%define smpvar smp
%endif
%ifarch i686 x86_64
%define xenvar xen0 xenU
%define kdumpvar kdump
%endif
%{!?kvariants: %define kvariants %{?upvar} %{?smpvar} %{?xenvar} %{?kdumpvar}}

Name:           %{kmod_name}-kmod
Version:        1.5
Release:        3.%(echo %{kverrel} | tr - _)
Summary:        %{kmod_name} kernel modules

Group:          System Environment/Kernel
License:        GPL
URL:            http://foo.sf.net
Source0:        http://download.sf.net/%{kmod_name}/%{kmod_name}-%{version}.tar.bz2
Patch0:         %{kmod_name}-foo.patch
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)

ExclusiveArch:  i586 i686 x86_64 ppc

%description
foo bar foobar

%{expand:%(%{kmodtool} rpmtemplate %{kmod_name} %{kverrel} %{kvariants} 2>/dev/null)}


%prep
%setup -q -c -T -a 0
pushd %{kmod_name}-%{version}*
%patch0 -b .patch0
popd
for kvariant in %{kvariants} ; do
cp -a %{kmod_name}-%{version} _kmod_build_${kvariant}
done


%build
for kvariant in %{kvariants}
do
ksrc=%{_usrsrc}/kernels/%{kverrel}${kvariant:+-$kvariant}-%{_target_cpu}
pushd _kmod_build_$kvariant
make -C "${ksrc}" SUBDIRS=${PWD} modules %{?_smp_mflags}
popd
done


%install
rm -rf $RPM_BUILD_ROOT
for kvariant in %{kvariants}
do
pushd _kmod_build_$kvariant
make install \
DESTDIR=$RPM_BUILD_ROOT
INST_DIR=$RPM_BUILD_ROOT/lib/modules/%{kverrel}${kvariant}/extra/%{kmod_name}
popd
done
chmod u+x $RPM_BUILD_ROOT/lib/modules/*/extra/%{kmod_name}/*


%clean
rm -rf $RPM_BUILD_ROOT


%changelog

So how does it work? let's go through important parts to describe them:


  • Source10: kmodtool

%define kmodtool sh %{SOURCE10}

Include kmodtool and Source10 and define a variable for it


  • %{!?kversion: %define kversion 2.6.21-1.2111_FC7}

The kernel-version for which the module will be build needs to be hardcoded for now. Hint: this can he overridden with "--define kversion foo" on the rpmbuild command line.

  • %define kmod_name foo

The name of the belonging userland package/the original name of the software. It is used in several places, therefore we define a macro for it.

  • %define kverrel  %(%{kmodtool} verrel  %{?kver} 2>/dev/null)

Define kverrel for the "kernel version release" (for example 2.6.21-1.1776_FC7 for kernel 2.6.21-1.1776_FC7smp). The kmodtool takes care here for stipping off "smp", "xen-guest" or other known variants from the sting %{?kver}. That is normally defined by the buildsys or the user to specify the kernel to build for. Kmodtool will use the string from $(uname -r) if kver is not defined.

  • %define upvar ""

%ifarch i586 i686 ppc %define smpvar smp %endif %ifarch i686 x86_64 %define xenvar xen0 xenU %define kdumpvar kdump %endif %{!?kvariants: %define kvariants %{?upvar} %{?smpvar} %{?xenvar} %{?kdumpvar}}

The kernel variants for which the module will be build need to be hardcoded for now. Define a variable kvariants with all the know kernel-variants for the current arch. Hint: this can he overridden with "--define kvariant foo bar" on the rpmbuild comand line.

  • Name:  %{kmod_name}-kmod

This is only the name for the SRPM -- the kernel module package itself is named the other way around, e.g. "Name: kmod-{kmod_name}" (see the rpm macro). This might be a bit confusing in the beginning, but solves some problems nicely.

  • Version: 1.5

This needs to be the same as in the userland package.

  • Release: 3.%(echo %{kver} | tr - _ )

This results in 3.2.6.21_1.1776_FC7 (e.g. the whole src.rpm is named something like "foo-kmod-1.5-3.2.6.21_1.1776_FC7").

The kver needs to be in the release to get proper debuginfo packages later. With this scheme we of course get a SRPM for each kernel we build the modules for. Therefore it might be better to create a stripped down tarball of the original source (e.g. remove the userland parts) to avoid wasting a lot of disk space. This is one of the drawbacks in this scheme, but it works.

  • ExclusiveArch: i586 i686 x86_64 ppc

This one is important for the buildsys. i386 should never be in the list because there is no i386 kernel in Fedora -- the buildsys would not find the BuildRequire and fail at that point. If a kernel module is only of interest for some of those archs of course feel free to list only those.

  • # magic hidden here:[BR] %{expand:%(%{kmodtool} rpmtemplate %{kmod_name} %{kverrel} %{kvariants} 2>/dev/null)}

Well, as the comment already says, the magic from kmodtool is buried here -- kmodtool is called with all relevant parameters and will output the part we showed in above example trough the function get_rpmtemplate. One or multiple such parts will get inserted depending on how many variants are passed to kmodtool it (in case the spec file is for example called with '--define "kvariants up smp"')

  • %prep

%setup -q -c -T -a 0

Use some fancy options from the %setup macro -q -- Quiet -c -- Create a subdir before extracing -T -- don't extract -a 0 -- extract Source0 after creating subdir

  • cd %{kmod_name}-%{version}

%patch0 -p0[BR] cd ..

Patch the extracted sources (if needed)

  • for kvariant in %{kvariants} ; do

cp -a foo-%{version} _kmod_build_$kvariant done

Create subdirs for each kernel variant

  • %build

for kvariant in %{kvariants} do ksrc=%{_usrsrc}/kernels/%{kverrel}${kvariant:+-$kvariant}-%{_target_cpu} cd _kmod_build_$kvariant make -C "${ksrc}" SUBDIRS=$PWD/foo modules %{?_smp_mflags} cd .. done

Build the module -- this or similar commands should work with most modern kernel modules.

  • %install

rm -rf $RPM_BUILD_ROOT for kvariant in %{kvariants} do make -C $dir install DESTDIR=$RPM_BUILD_ROOT INST_DIR=$RPM_BUILD_ROOT/lib/modules/%{kverrel}${kvariant}/extra/%{kmod_name} done chmod u+x $RPM_BUILD_ROOT/lib/modules/*/extra/%{kmod_name}/*

Install the module and mark it executable for stripping.

Mini-FAQ

What's the best way to test if the spec file works fine

Build the kmod with rpmbuild and in mock for a kernel with a different version than the one that are running currently, e.g., if you running 2.6.21-1.2122_FC7 try to build for 2.6.21-1.2111_FC7.

Will there be further enhancements for the kernel module proposal?

Probably yes. We probably didn't consider every possible scenario out there. But the general scheme/direction probably will stay.

Will other repos use the same scheme?

You have to ask those repos. One popular 3rd party repo that enhances Core and Extras uses it currently.

Does it work with yum?

Not perfectly (yet). We need a plugin for yum that will handles some special cases for kernel module packages. It is available in Extras currently, but not perfect yet.

Does it work in the Extras Buildsys?

Not perfectly (yet). We need a plugin or enhancement that will search for the latest kernel version and then will pass this to the rpmbuild command with "--define 'kversion foo'". There are no concrete plans who works on that ATM. Are you interested to help?

Will this proposal be used for the GFS stuff in Fedora Core, too?

That's the plan.

Is it possible to compile against self compiled kernels?

Yes, if self compiled means "packaged in a manner compatible with FC kernels". Otherwise: no. Well, it's _possible_, but not supported nor documented. Just create a FC compatible kernel package of your custom kernel, and compile the module packages for it by passing --define 'variant foo' to rpmbuild.

This standard is stupid.

Maybe. Post a better one. (No offense, more a FYI: We invested a lot of time in this standard and had to make a lot of compromises to make everyone happy -- doing bigger changes just "because I like my scheme better" probably won't help. But technical advantages that are documented and already tested in real life might convince us).

How do I rebuild a kmod srpm for one kernel

There are several ways:

Build foo-kmod.src for UP-Kernel 2.6.21-1.2139_FC7 i686:

$ rpmbuild --rebuild foo-kmod.src --define 'kversion 2.6.21-1.2139_FC7' --define 'kvariants ""' --target i686

Build foo-kmod.src for UP-Kernel 2.6.21-1.2139_FC7 x86_64:

$ rpmbuild --rebuild foo-kmod.src --define 'kversion 2.6.21-1.2139_FC7' --define 'kvariants ""' --target x86_64

Build foo-kmod.src for SMP-Kernel 2.6.21-1.2139_FC7 i686:

$ rpmbuild --rebuild foo-kmod.src --define 'kversion 2.6.21-1.2139_FC7' --define 'kvariants smp' --target i686

Build foo-kmod.src for UP- and SMP-kernel 2.6.21-1.2139_FC7 i686:

$ rpmbuild --rebuild foo-kmod.src --define 'kversion 2.6.21-1.2139_FC7' --define 'kvariants "" smp'  --target i686

Note: you can't rebuild kmod's for i386 because there is no i386 kernel in Fedora. So on x86 you always have to use --target i686 or --target i586 when rebuilding kmod's.


Examples

Header for dists

The first part of the spec files needs adjustments to make sure that kmod's are build for all kernels. Here are the current up2date version:

For FC6:

%{!?kversion: %define kversion 2.6.16-1.2111_FC6}

%define kmod_name foo
%define kverrel %(%{kmodtool} verrel %{?kversion} 2>/dev/null)

%define upvar ""
%ifarch i686
%define paevar PAE
%endif
%ifarch ppc
%define smpvar smp
%endif
%ifarch i686 x86_64
%define xenvar xen
%define kdumpvar kdump
%endif
%{!?kvariants: %define kvariants %{?upvar} %{?smpvar} %{?paevar} %{?xenvar} %{?kdumpvar}}