Edit Info Other
Login

Kmods2"

Differences between revisions 4 and 5
Revision 4 as of 2007-11-24 12:30:03
Size: 19643
Comment: base for updated version
Revision 5 as of 2007-11-24 18:32:08
Size: 25889
Comment:
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:
= Packaging of kernel modules with the kmods-Standard 1.x = = Packaging of kernel modules with the kmod-Standard 2 =
Line 6: Line 6:
== Introduction ==

/!\ Note: This Document was copied from the fedoraproject wiki and only slightly adjusted, as RPM Fusion for kmod-packages will use the second kmod version in its repos for Fedora 8 and later

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 the kmod concept 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.
= 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|Engineering) Steering Commitee (FESCo) in 2005/2006 worked out the kmod concept for packaging kernel-modules in Fedora. By mid 2007 packages with kernel modules (like kmods or dkms) were completely banned from Fedora. Livna developers (which started shipping kmod pacakges soon after FESCo developed the concept) at that point picked up the kmod standard and improved it for Fedora 8.

This document describes the enhanced version which is also known as kmod 2.x. The [[:Packaging/KernelModules/Kmods1|description of the first version]] can be found in this wiki as well. The initial version of this document further is based on that document, thus you can use the wiki-internal diff tools to see the differences by comparing the first version with the latest one. You can also read the [[#V1vsV2|section that explains the main differences]] roughly.

== Split ==

There are always at least two SRPMS when a kernel-module gets packaged -- one builds a userland package from the source while the other builds packages with *only* the kernel-module(s) in it. That way new kernel-modules (build for newer kernels or modified because new patches were needed) can be shipped without shipping new userland packages, which avoids unnecessary downloads and updates for our users.
Line 16: Line 18:
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 binary packages build from the userland SRPM contains tools, documentation, license files, 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.
Line 20: Line 22:
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}'

 1. 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
The userland packages need to follow the usual Fedora 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 'Requires: %{name}-kmod >= %{version}'

 1. The package must provide %{name}-kmod-common using 'Provides: %{name}-kmod-common = %{version}'; it possible to name the package directly %{name}-kmod-common, but it should be avoided, as that's meaningless for users and confusing.
Line 28: Line 30:
Of course all general rules that apply to rpm packaging in Fedora 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 [http://cvs.fedora.redhat.com/viewcvs/kmodtool/kmodtool?root=fedora&view=markup 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)
Besides the normal packaging rules there are several additional rules for the package that contains the modules, which is called kmod package from now on. Instead of writing all those down a specfile template was created.

That template (and thus all kmod packages) make use of a bash script called kmodtool, which normally should be in a package called kmodtool that gets provided by the repo. There are further buildsys-build-<repo>-{newest,current} packages needed, which is used when the kmods get build in the buildsys of the repo; those pacakges holds the informations for which kernel versions the kmods get build.

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

==== kmod template ====

{{{
# (un)define the next line to either build for the newest or all current kernels

%define buildforkernels newest

%define repo foo



Name: foo-kmod

Version: foo
Release: foo
Summary: Kernel module(s)


Group: System Environment/Kernel

License: foo
URL: http://foo
Source0: http://foo/foo.foo
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)

BuildRequires: %{_bindir}/kmodtool


# needed for plague to make sure it builds for i586 and i686

ExclusiveArch: i586 i686 x86_64 ppc
 ppc64

# get the proper build-sysbuild package from the repo, which
# tracks in all the kernel-devel packages
%{!?kernels:BuildRequires: buildsys-build-%{repo}-kerneldevpkgs-%{?buildforkernels:%{buildforkernels}}%{!?buildforkernels:current}-%{_target_cpu} }


# kmodtool does its magic here

%{expand:%(kmodtool --target %{_target_cpu} --repo %{repo} --kmodname %{name} %{?buildforkernels:--%{buildforkernels}} %{?kernels:--for-kernels "%{?kernels}"} 2>/dev/null) }



%description

foo


%prep

# error out if there was something wrong with kmodtool

%{?kmodtool_check}

# print kmodtool output for debugging purposes:

kmodtool --target %{_target_cpu} --repo %{repo} --kmodname %{name} %{?buildforkernels:--%{buildforkernels}} %{?kernels:--for-kernels "%{?kernels}"} 2>/dev/null

%setup -q -c -T -a 0

# pushd foo-%{version}
# #patch0 -p1

# popd

for kernel_version in %{kernel_versions} ; do

    cp -a foo-%{version} _kmod_build_${kernel_version%%___*}

done



%build

for kernel_version in %{kernel_versions}; do

    make %{?_smp_mflags} -C "${kernel_version##*___}" SUBDIRS=${PWD}/_kmod_build_${kernel_version%%___*} modules

done




%install

rm -rf ${RPM_BUILD_ROOT
}
for kernel_version in %{kernel_versions}; do

    make install DESTDIR=${RPM_BUILD_ROOT} KMODPATH=%{kmodinstdir_prefix}/${kernel_version%%___*}/%{kmodinstdir_postfix}
    # install -D -m 755 _kmod_build_${kernel_version%%___*}/foo/foo.ko ${RPM_BUILD_ROOT}%{kmodinstdir_prefix}/${kernel_version%%___*}/%{kmodinstdir_postfix}/foo.kmod
done



%clean

rm -rf $RPM_BUILD_ROOT




%changelog

}}}

So how does it work? let's go through important parts and describe the different bits:

 * {{{
# (un)define the next line to either build for the newest or all current kernels

%define buildforkernels newest


}}}

 This part decides what kernels to build for. Normally this should look like above; that way each time a new kernel from Fedora get released the kmod can be rebuild for them. And only for those -- that way you don't rebuild the kmods for the kernel-xen when a new stock kernel gets released and vice versa.

 If you release a new version that disable the define like this:

 {{{
# (un)define the next line to either build for the newest or all current kernels

#define buildforkernels newest
 

}}}

 That way the kmod will be build for latest kernel and kernel-xen versions. After that undo that change to prepare for the next rebuild.

 * {{{
%define repo foo


}}}

 Not strictly needed, but makes life a bit easier

 *{{{
# needed for plague to make sure it builds for i586 and i686

ExclusiveArch: i586 i686 x86_64 ppc
 ppc64
}}}

 These are needed for plague; without it would try to build the kmod for a i386 kernel on x86-32, which is not available.

 * {{{
# get the proper build-sysbuild package from the repo, which
# tracks in all the kernel-devel packages
%{!?kernels:BuildRequires: buildsys-build-%{repo}-kerneldevpkgs-%{?buildforkernels:%{buildforkernels}}%{!?buildforkernels:current}-%{_target_cpu} }

}}}

 This line manages the BuildRequires. If user specified the "kernels" macro, then it does nothing -- kmodtool will manage the build requires if they are needed. If the "kernels" macro is not set either buildsys-build-%{repo}-kerneldevpkgs-newest or buildsys-build-%{repo}-kerneldevpkgs-current will be a BuildRequire. Those should be managed by the repo admins and depend on the newest or current/latest kernels.

 * {{{
# kmodtool does its magic here

%{expand:%(kmodtool --target %{_target_cpu} --repo %{repo} --kmodname %{name} %{?buildforkernels:--%{buildforkernels}} %{?kernels:--for-kernels "%{?kernels}"} 2>/dev/null) }


}}}

 Here kmodtool does its magic -- a lot of, thus see the next section below for the details.

 * {{{
%prep
# error out if there was something wrong with kmodtool

%{?kmodtool_check}

}}}

 This is not strictly needed, but kmodtool sometimes fails and then put informations about it in the kmodtool_check macro for debugging purposes.

 * {{{
# print kmodtool output for debugging purposes:

kmodtool --target %{_target_cpu} --repo %{repo} --kmodname %{name} %{?buildforkernels:--%{buildforkernels}} %{?kernels:--for-kernels "%{?kernels}"} 2>/dev/null

}}}

 This is not strictly needed either, but just outputting all the stuff kmodtool did to the console can make debugging easier. Note that this call should always be identical with the earlier komtool call, except the "expand" stuff around it

 * {{{
%setup -q -c -T -a 0

# pushd foo-%{version}
# #patch0 -p1

# popd

for kernel_version in %{kernel_versions} ; do

    cp -a foo-%{version} _kmod_build_${kernel_version%%___*}

done

}}}
 This extracts source0; you can apply patches afterwards if you want to. Then a directory for each kmod build is being created.

 Please note the "%{kernel_versions}" and "${kernel_version%%___*}". The first one gets set by kmodtool and creates a list of kernels and where their development files get found; the two values per kernel get separated by the string "___", which makes it possible to easily reference the first or the second part of it in bash. This example bash code explains it better then words can do:

 {{{
kernel_versions="2.6.23.1-42.fc8___/usr/src/kernels/2.6.23.1-42.fc8-x86_64/ 2.6.23.1-42.fc8pae___/usr/src/kernels/2.6.23.1-42.fc8pae-x86_64/ "

for kernel_version in ${kernel_versions} ; do

    echo "The builddir for ${kernel_version%%___*} is ${kernel_version##*__}"

done
}}}

 Running it will lead to this output:

 {{{
The builddir for 2.6.23.1-42.fc8 is /usr/src/kernels/2.6.23.1-42.fc8-x86_64/

The builddir for 2.6.23.1-42.fc8pae is /usr/src/kernels/2.6.23.1-42.fc8pae-x86_64/
}}}

 * {{{
%build

for kernel_version in %{kernel_versions}; do

    make %{?_smp_mflags} -C "${kernel_version##*___}" SUBDIRS=${PWD}/_kmod_build_${kernel_version%%___*} modules

done
}}}

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

 * {{{
%install

rm -rf ${RPM_BUILD_ROOT
}
for kernel_version in %{kernel_versions}; do

    make install DESTDIR=${RPM_BUILD_ROOT} KMODPATH=%{kmodinstdir_prefix}/${kernel_version%%___*}/%{kmodinstdir_postfix}
${kernel_version%%___*}/%{kmodinstdir_postfix}/foo.kmod
done
chmod u+x ${RPM_BUILD_ROOT}/lib/modules/*/extra/*/*

}}}

 Install the module with "make install" and mark it executable for stripping. Note the new macros kmodinstdir_prefix (normally /lib/modules/) and kmodinstdir_postfix (normally /extra/ntfs/), that get set by kmodtool properly.

 Sometimes it's easier to just install the module manually; then use something like the following instead:

{{{
%install

rm -rf ${RPM_BUILD_ROOT
}
for kernel_version in %{kernel_versions}; do

    install -D -m 755 _kmod_build_${kernel_version%%___*}/foo/foo.ko ${RPM_BUILD_ROOT}%{kmodinstdir_prefix}/foo.ko
done
chmod u+x ${RPM_BUILD_ROOT}/lib/modules/*/extra/*/*

}}}

 But using "make install" should be preferred, as you that way will get a new module automatically should upstream suddenly add another one.

==== kmodtool ====

Kmodtool is a simple bash script that creates parts of the spec file dynamically depending on how you call it -- that's why the kmod template above has no files section. Kmodtool gets called like this

{{{
# kmodtool does its magic here

%{expand:%(kmodtool --target %{_target_cpu} --repo %{repo} --kmodname foo %{?buildforkernels:--%{buildforkernels}} %{?kernels:--for-kernels "%{?kernels}"} 2>/dev/null) }


}}}

Kmodtool itself is no real magic -- if you understand a bit of bash scripting you should be able to see how it works. It created three major parts: one that defines some macros, a meta-package kmod-foo per kernel variant and a package kmod-foo-<kernel-version> per kernel variant which contains the module itself and gets tracked in for new kernels by new kmod-foo meta package.

===== kmod-foo-<kernel-version> package =====

The most important part of kmodtool is the function print_rpmtemplate_per_kmodpkg, which contains a template for the package which will hold the kmod later:

{{{
%package -n kmod-${kmodname}-${verrel}${variant}

Summary: ${kmodname} kernel module(s) for ${verrel}${variant}
Line 44: Line 329:
Provides: kernel-modules = ${verrel}${variant}
Provides: ${kmod_name}-kmod = %{?epoch:%{epoch}:}%{version}-%{release}

Provides: kernel-modules-for-kernel = ${verrel}${variant}

Provides: ${kmodname}-kmod = %{?epoch:%{epoch}:}%{version}-%{release}

Requires: ${kmodname}-kmod-common >= %{?epoch:%{epoch}:}%{version}

Requires(post): /sbin/depmod

Requires(postun): /sbin/depmod
Line 47: Line 341:
Requires: ${kmod_name}-kmod-common >= %{?epoch:%{epoch}:}%{version} BuildRequires: kernel${dashvariant}-devel-%{_target_cpu} = ${verrel}


%post -n kmod-${kmodname}-${verrel}${variant}

/sbin/depmod -aeF /boot/System.map-${verrel}${variant} ${verrel}${variant} > /dev/null || :

%postun -n kmod-${kmodname}-${verrel}${variant}

/sbin/depmod -aF /boot/System.map-${verrel}${variant} ${verrel}${variant} &> /dev/null || :

%description -n kmod-${kmodname}-${verrel}${variant}

This package provides the ${kmodname} kernel modules built for the Linux

kernel ${verrel}${variant} for the %{_target_cpu} family of processors.


%files -n kmod-${kmodname}-${verrel}${variant}

%defattr(644,root,root,755)

/lib/modules/${verrel}${variant}/extra/${kmodname}/

}}}

This macro later expands to something like the following when building for a standard kernel 2.6.23.1-42.fc8 on i686 and %{version} = 1.5; the output get inserted into the spec file before building :

{{{
%package -n kmod-foo-2.6.23.1-42.fc8

Summary: foo kernel module(s) for 2.6.23.1-42.fc8

Group: System Environment/Kernel

Provides: kernel-modules-for-kernel = 2.6.23.1-42.fc8

Provides: foo-kmod = 1.5-1
Requires: foo-kmod-common >= 1.5-1
Line 49: Line 381:
Line 50: Line 383:
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}



Requires: kernel-i686 = 2.6.23.1-42.fc8

BuildRequires: kernel-devel- i686 = 2.6.23.1-42.fc8

%post -n kmod-foo-2.6.23.1-42.fc8

/sbin/depmod -aeF /boot/System.map-2.6.23.1-42.fc8 2.6.23.1-42.fc8 > /dev/null || :

%postun -n kmod-foo-2.6.23.1-42.fc8

/sbin/depmod -aF /boot/System.map-2.6.23.1-42.fc8 2.6.23.1-42.fc8 &> /dev/null || :



%description -n kmod-foo-2.6.23.1-42.fc8

This package provides the foo kernel modules built for the Linux

kernel 2.6.23.1-42.fc8 for the %{_target_cpu} family of processors.


%files -n kmod-foo-2.6.23.1-42.fc8
Line 60: Line 410:
/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

/lib/modules/2.6.23.1-42.fc8/extra/foo/

}}}

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

 * {{{
%package -n kmod-foo-2.6.23.1-42.fc8

}}}

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

 * {{{
Provides: kernel-modules-for-kernel = 2.6.23.1-42.fc8

}}}

Via this provides depsolvers or scripts can check for which kernel a module was build.

 * {{{
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.23.1-42.fc8
}}}

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.23.1-42.fc8
}}}

Needed for building kernel-modules. This is just here if we are building for a Fedora kernel and will not show up when building for a custom kernel

 * {{{
Line 85: Line 458:
/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)}}}
}}}
Line 119: Line 462:
 * {{{/lib/modules/2.6.21-1.1776_FC7/extra/foo/}}}  * {{{
/lib/modules/2.6.23.1-42.fc8/extra/foo/
}}}
Line 123: Line 468:
==== kernel module specfile ====

An example kernel module specfile for "foo" might look like this one: [[attachment: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):

{{{
# stuff to be implemented externally:
Source10: kmodtool
%define kmodtool bash %{SOURCE10}
# end stuff to be ...

# hardcode for now:
%{!?kversion: %define kversion 2.6.21-1.2111_FC7}
# hint: this can he overridden with "--define kversion foo" on the rpmbuild command line, e.g.
# --define "kversion 2.6.21-1.2096_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}}
# hint: this can he overridden with "--define kvariant foo bar" on the rpmbuild command line, e.g.
# --define 'kvariant "" smp'

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

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


%prep
# to understand the magic better or to debug it, uncomment this:
#{kmodtool} rpmtemplate %{kmod_name} %{kverrel} %{kvariants} 2>/dev/null
#sleep 5
%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
# Temporarily executable for stripping, fixed later in %%files.
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
 # Temporarily executable for stripping, fixed later in %%files.
 chmod u+x $RPM_BUILD_ROOT/lib/modules/*/extra/%{kmod_name}/*

Install the module and mark it executable for stripping.
===== kmod-foo =====

This is a meta-package which only purpose is to depend on the latest kmod-foo-<kernel-version> package. Without this meta-pacakge the later would be tracked in automatically for newly released kernels, because yum can't know that kmod-ntfs-2.6.23.1-49.fc8.x86_64 is a update for kmod-ntfs-2.6.23.1-42.fc8.x86_64 and that the latter should remain installed.

The meta-package gets created in the function print_rpmtemplate_kmodmetapkg from kmodtool. It will create a package which looks like this:

{{{
%package -n kmod-foo
Summary: Metapackage which tracks in foo kernel module for newest kernel
Group: System Environment/Kernel
Requires: kmod-foo-2.6.23.1-49.fc8

%description -n kmod-foo
This is a meta-package without payload which sole purpose is to require the
foo kernel module(s) for the newest kernel,
to make sure you get it together with a new kernel.

%files -n kmod-foo
%defattr(644,root,root,755)
}}}

===== macros =====

Kmotool further creates three macros that can be used in the spec file during the %prep, %build and %install stages.

{{{
%define kmodinstdir_prefix /lib/modules/
%define kmodinstdir_postfix /extra/ntfs/
%define kernel_versions 2.6.23.1-49.fc8___%{_usrsrc}/kernels/2.6.23.1-49.fc8-%{_target_cpu} 2.6.23.1-49.fc8PAE___%{_usrsrc}/kernels/2.6.23.1-49.fc8PAE-%{_target_cpu}
}}}

Look above in the kmod spec file template for details how to use those three.

[[Anchor (V1vsV2)]]
== kmods v1 versus v2 ==

There were several design issues that lead to the the overhaul named kmods v2:

 * kmods v1 could only build for different variants ("" (UP/Standard), SMP, Xen, ...) of one kernel-version (say 2.6.21-1.1776_FC7) in one build cycle. That became hindering when Fedora introduced a dedicated kernel-xen package with a different EVR than the main kernel package. The new kmods version can build for different kernel-versions and variants easily and allows skipping certain variants (say for example xen) a lot easier.

 * FESCo explicitly wanted to builds kmods only the latest kernel versions when they designed kmods v1; but sometimes there are situations where a kmod for a older kernel might be handy or needed, thus the new kmods standard allows to build for old and new kernels during one build cycle as well. To make is poissble for users to get the kmods for older kernels the names need to be different, wich lead to slightly ugly package names for kmods, as they contaain the kernel-version now in in the name (e.g. kmod-foo-2.6.23.8-62.fc8-1.0-1, where kmod-foo-2.6.23.8-62.fc8 is the actually %{name}), but that solves some corner cases with dep-solvers as well.

 * the '--define "kversions foo" --define "kvariants bar"' systax when rebuilding kmods v1 was not very intuitive for people that wanted to rebuild kmods; '--define "kernels $(uname -r)"' is a lot easier to remember and can be scripted more easily

 * kmodtool and a big kmodtool specific header with a lot of macros was part of many packages directly, which was not easily to maintain and looked ugly. kmodtool is now in a separate package and only needed when it comes to building the kmods (beforehand it was needed needed to determine the BuildRequires as well)
Line 318: Line 515:
=== Is it possible to compile against self compiled or other kernels? ===

Yes, easily, it's just a few steps. If you don't have a rpm build environment set one up like this
{{{
$ su -c "yum -y install rpmdevtools kmodtool kernel-devel"
$ rpmdev-setuptree
}}}

Now download the kmod src.rpm to the local directory and rebuild it for the running kernel:
{{{
$ yumdownloader --source kmod-foo
$ rpmbuild --rebuild foo-kmod*.src.rpm --define "kernels $(uname -r)" --target $(uname -m)
}}}

At the end of the build output you will see the names of the RPMs rpmbuild built. Use yum to install it with a command like this
{{{
$ su -c "yum --nogpgcheck install ~/rpmbuild/RPMS/i686/kmod-foo-1.0-4.i686.rpm"
}}}

Some notes, as above commands need some adjustments often:

 * instead of foo insert the name of the kmod you want to rebuild

 * the package rpmdevtools installed in the first command contains the script rpmdev-setuptree, which creates a environment for building rpms in your home directory (~/.rpmmacros and ~/rpmbuild)

 * the package kmodtool installed in the first command is needed for building kmods

 * if you use a special kernel variant like xen or PAE you need to install kernel-PAE-devel or kernel-xen-devel instead of kernel-devel in the first command; these packages contain the files that are needed to build kernel modules for the kernels shipped by Fedora.

 * note that yum will install the latest version of the kernel-devel packages for the latest Fedora kernels; if you didn't update and restart your system often please make sure you are running a kernel that is matching the version of the kernel-devel package

 * if you want to build a kmod for a kernel you compiled yourself you may skip installing the kernel-devel package

 * note third command in the "[...]--rebuild foo-kmod*.src.rpm[...]" part contains a "*" -- normally that should match the package yumdownloader downloaded earlier, but if you have old versions around in the local directory be sure to just pick the latest one

 * instead of "$(uname -r)" you can use one or multiple kernel versions; e.g. "--define "kernels 2.6.23.1-42.fc8 2.6.23.1-42.fc8PAE 2.6.21-2949.fc8xen" will build three kmod packages

 * the "--target $(uname -m)" in the third command actually is only needed on x86-32 (i386) systems

 * the "--nogpgcheck" in the last command is needed, because the package that was just build is unsigned

 * using yum to install will make sure you get the package with the userland stuff (that contins the virtual provides "foo-kmod-common") from the repos.
Line 321: Line 560:
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. Build the kmod (with and without mock) against a different kernel then the one that is running on your system. 
Line 327: Line 566:
=== 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.
=== Do I need a special plugin for yum or other depsolvers? ===

It works fine without any plugins. Maybe a plugin will be written to fix two corner cases:

 * Case 1: if yum sees the newly releases kmods from a 3rd party repo for a newly released Fedora kernel before the Fedora mirror yum selected offers that new Fedora kernel; this leads to a "dependency not met" error by yum

 * Case 2: yum installs a newly released kernel but doesn't install the kmods for it, as they are not yet available in the repos or the mirror used; if the user reboots now the kmods will be missing, which can lead to problems as for example some popular graphic drivers will not work if the proper kernel modules are missing. Once the modules are available they can be easily installed by a simple "yum update".

=== Does it require special support in the buildsys of a repo? ===

No. There is just a minor glitch in some versions of mock that makes building for older kernels a two-cycle build (if you BuildRequire both kernel-devel-2.6.23.1-42.fc8 and kernel-devel-2.6.23.1-49.fc8 in a package mock/yum will only install kernel-devel-2.6.23.1-49.fc8)
Line 351: Line 580:
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 ==

 * lirc: [http://cvs.fedora.redhat.com/viewcvs/rpms/lirc/devel/lirc.spec?root=extras&view=markup main userland], [http://cvs.fedora.redhat.com/viewcvs/rpms/lirc-kmod-common/devel/lirc-kmod-common.spec?root=extras&view=markup kernel-dependent userland], [http://cvs.fedora.redhat.com/viewcvs/rpms/lirc-kmod/devel/lirc-kmod.spec?root=extras&view=markup kmod]
 * thinkpad: [http://cvs.fedora.redhat.com/viewcvs/rpms/thinkpad-kmod-common/devel/thinkpad-kmod-common.spec?root=extras&rev=1.3&view=markup userland], [http://cvs.fedora.redhat.com/viewcvs/rpms/thinkpad-kmod/devel/thinkpad-kmod.spec?root=extras&view=markup kmod]
 * There are also some kmod-packages in a major 3rd party repo that enhances Core and Extras

=== 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:

{{{
# hardcode for now:
%{!?kversion: %define kversion 2.6.16-1.2111_FC6}
# hint: this can he overridden with "--define kversion foo" on the rpmbuild command line, e.g.
# --define "kversion 2.6.16-1.2096_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}}
# hint: this can he overridden with "--define kvariant foo bar" on the rpmbuild command line, e.g.
# --define 'kvariant "" smp'
}}}
Maybe. Post a better one. No offense, more a FYI: FESCo and some people from Livna and RPM Fusion 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 get accepted. But technical advantages that are properly documented (and maybe even tested in real life) might convince us. But remember, the improvements need to be worth converting the existing packages over.

=== Why not simply use dkms ===

The short answer: Dkms especially for experienced users that often change kernels is a nice concept that also has a lot of usage cases for enterprise customers. Thus repos should consider to ship dkms packages in parallel to kmods. But for ordinary users shipping a pre-compiled module has many averages instead of doign a Gentoo-Like concept.

=== Examples ===

A good example for the kmod v2 stuff are the madiwfi/madwifi-kmod package from Livna; the ntfs packages are also a good starting point. For an example of the "skip the build for the xen kernel" take a look at the kmods for the graphic drivers from AMD and Nvidia.

Packaging of kernel modules with the kmod-Standard 2

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|Engineering) Steering Commitee (FESCo) in 2005/2006 worked out the kmod concept for packaging kernel-modules in Fedora. By mid 2007 packages with kernel modules (like kmods or dkms) were completely banned from Fedora. Livna developers (which started shipping kmod pacakges soon after FESCo developed the concept) at that point picked up the kmod standard and improved it for Fedora 8.

This document describes the enhanced version which is also known as kmod 2.x. The description of the first version can be found in this wiki as well. The initial version of this document further is based on that document, thus you can use the wiki-internal diff tools to see the differences by comparing the first version with the latest one. You can also read the section that explains the main differences roughly.

Split

There are always at least two SRPMS when a kernel-module gets packaged -- one builds a userland package from the source while the other builds packages with *only* the kernel-module(s) in it. That way new kernel-modules (build for newer kernels or modified because new patches were needed) can be shipped without shipping new userland packages, which avoids unnecessary downloads and updates for our users.

userland package

The binary packages build from the userland SRPM contains tools, documentation, license files, 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 need to follow the usual Fedora 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 'Requires: %{name}-kmod >= %{version}'

  2. The package must provide %{name}-kmod-common using 'Provides: %{name}-kmod-common = %{version}'; it possible to name the package directly %{name}-kmod-common, but it should be avoided, as that's meaningless for users and confusing.

kernel-module package

Besides the normal packaging rules there are several additional rules for the package that contains the modules, which is called kmod package from now on. Instead of writing all those down a specfile template was created.

That template (and thus all kmod packages) make use of a bash script called kmodtool, which normally should be in a package called kmodtool that gets provided by the repo. There are further buildsys-build-<repo>-{newest,current} packages needed, which is used when the kmods get build in the buildsys of the repo; those pacakges holds the informations for which kernel versions the kmods get build.

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

kmod template

# (un)define the next line to either build for the newest or all current kernels

%define buildforkernels newest

%define repo foo



Name:           foo-kmod

Version:        foo
Release:        foo
Summary:        Kernel module(s)


Group:          System Environment/Kernel

License:        foo
URL:            http://foo
Source0:        http://foo/foo.foo
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)

BuildRequires:  %{_bindir}/kmodtool


# needed for plague to make sure it builds for i586 and i686

ExclusiveArch:  i586 i686 x86_64 ppc
 ppc64

# get the proper build-sysbuild package from the repo, which
# tracks in all the kernel-devel packages
%{!?kernels:BuildRequires: buildsys-build-%{repo}-kerneldevpkgs-%{?buildforkernels:%{buildforkernels}}%{!?buildforkernels:current}-%{_target_cpu} }


# kmodtool does its magic here

%{expand:%(kmodtool --target %{_target_cpu} --repo %{repo} --kmodname %{name} %{?buildforkernels:--%{buildforkernels}} %{?kernels:--for-kernels "%{?kernels}"} 2>/dev/null) }



%description

foo


%prep

# error out if there was something wrong with kmodtool

%{?kmodtool_check}

# print kmodtool output for debugging purposes:

kmodtool  --target %{_target_cpu}  --repo %{repo} --kmodname %{name} %{?buildforkernels:--%{buildforkernels}} %{?kernels:--for-kernels "%{?kernels}"} 2>/dev/null

%setup -q -c -T -a 0

# pushd foo-%{version}
# #patch0 -p1

# popd

for kernel_version in %{kernel_versions} ; do

    cp -a foo-%{version} _kmod_build_${kernel_version%%___*}

done



%build

for kernel_version in %{kernel_versions}; do

    make %{?_smp_mflags} -C "${kernel_version##*___}" SUBDIRS=${PWD}/_kmod_build_${kernel_version%%___*} modules

done




%install

rm -rf ${RPM_BUILD_ROOT
}
for kernel_version in %{kernel_versions}; do

    make install DESTDIR=${RPM_BUILD_ROOT} KMODPATH=%{kmodinstdir_prefix}/${kernel_version%%___*}/%{kmodinstdir_postfix}
    # install -D -m 755 _kmod_build_${kernel_version%%___*}/foo/foo.ko ${RPM_BUILD_ROOT}%{kmodinstdir_prefix}/${kernel_version%%___*}/%{kmodinstdir_postfix}/foo.kmod
done



%clean

rm -rf $RPM_BUILD_ROOT




%changelog

So how does it work? let's go through important parts and describe the different bits:

  • # (un)define the next line to either build for the newest or all current kernels
    
    %define buildforkernels newest
    This part decides what kernels to build for. Normally this should look like above; that way each time a new kernel from Fedora get released the kmod can be rebuild for them. And only for those -- that way you don't rebuild the kmods for the kernel-xen when a new stock kernel gets released and vice versa. If you release a new version that disable the define like this:
    # (un)define the next line to either build for the newest or all current kernels
    
    #define buildforkernels newest
     
    That way the kmod will be build for latest kernel and kernel-xen versions. After that undo that change to prepare for the next rebuild.
  • %define repo foo
    Not strictly needed, but makes life a bit easier
  • # needed for plague to make sure it builds for i586 and i686
    
    ExclusiveArch:  i586 i686 x86_64 ppc
     ppc64
    These are needed for plague; without it would try to build the kmod for a i386 kernel on x86-32, which is not available.
  • # get the proper build-sysbuild package from the repo, which
    # tracks in all the kernel-devel packages
    %{!?kernels:BuildRequires: buildsys-build-%{repo}-kerneldevpkgs-%{?buildforkernels:%{buildforkernels}}%{!?buildforkernels:current}-%{_target_cpu} }

    This line manages the BuildRequires. If user specified the "kernels" macro, then it does nothing -- kmodtool will manage the build requires if they are needed. If the "kernels" macro is not set either buildsys-build-%{repo}-kerneldevpkgs-newest or buildsys-build-%{repo}-kerneldevpkgs-current will be a BuildRequire. Those should be managed by the repo admins and depend on the newest or current/latest kernels.

  • # kmodtool does its magic here
    
    %{expand:%(kmodtool --target %{_target_cpu} --repo %{repo} --kmodname %{name} %{?buildforkernels:--%{buildforkernels}} %{?kernels:--for-kernels "%{?kernels}"} 2>/dev/null) }
    Here kmodtool does its magic -- a lot of, thus see the next section below for the details.
  • %prep
    # error out if there was something wrong with kmodtool
    
    %{?kmodtool_check}
    This is not strictly needed, but kmodtool sometimes fails and then put informations about it in the kmodtool_check macro for debugging purposes.
  • # print kmodtool output for debugging purposes:
    
    kmodtool  --target %{_target_cpu}  --repo %{repo} --kmodname %{name} %{?buildforkernels:--%{buildforkernels}} %{?kernels:--for-kernels "%{?kernels}"} 2>/dev/null
    This is not strictly needed either, but just outputting all the stuff kmodtool did to the console can make debugging easier. Note that this call should always be identical with the earlier komtool call, except the "expand" stuff around it
  • %setup -q -c -T -a 0
    
    # pushd foo-%{version}
    # #patch0 -p1
    
    # popd
    
    for kernel_version in %{kernel_versions} ; do
    
        cp -a foo-%{version} _kmod_build_${kernel_version%%___*}
    
    done
    This extracts source0; you can apply patches afterwards if you want to. Then a directory for each kmod build is being created.

    Please note the "%{kernel_versions}" and "${kernel_version%%_*}". The first one gets set by kmodtool and creates a list of kernels and where their development files get found; the two values per kernel get separated by the string "_", which makes it possible to easily reference the first or the second part of it in bash. This example bash code explains it better then words can do:

    kernel_versions="2.6.23.1-42.fc8___/usr/src/kernels/2.6.23.1-42.fc8-x86_64/ 2.6.23.1-42.fc8pae___/usr/src/kernels/2.6.23.1-42.fc8pae-x86_64/ "
    
    for kernel_version in ${kernel_versions} ; do
    
        echo "The builddir for ${kernel_version%%___*} is ${kernel_version##*__}"
    
    done
    Running it will lead to this output:
    The builddir for 2.6.23.1-42.fc8 is /usr/src/kernels/2.6.23.1-42.fc8-x86_64/
    
    The builddir for 2.6.23.1-42.fc8pae is /usr/src/kernels/2.6.23.1-42.fc8pae-x86_64/
  • %build
    
    for kernel_version in %{kernel_versions}; do
    
        make %{?_smp_mflags} -C "${kernel_version##*___}" SUBDIRS=${PWD}/_kmod_build_${kernel_version%%___*} modules
    
    done
    Build the module -- this or similar commands should work with most modern kernel modules.
  • %install
    
    rm -rf ${RPM_BUILD_ROOT
    }
    for kernel_version in %{kernel_versions}; do
    
        make install DESTDIR=${RPM_BUILD_ROOT} KMODPATH=%{kmodinstdir_prefix}/${kernel_version%%___*}/%{kmodinstdir_postfix}
    ${kernel_version%%___*}/%{kmodinstdir_postfix}/foo.kmod
    done
    chmod u+x ${RPM_BUILD_ROOT}/lib/modules/*/extra/*/*
    Install the module with "make install" and mark it executable for stripping. Note the new macros kmodinstdir_prefix (normally /lib/modules/) and kmodinstdir_postfix (normally /extra/ntfs/), that get set by kmodtool properly. Sometimes it's easier to just install the module manually; then use something like the following instead:

%install

rm -rf ${RPM_BUILD_ROOT
}
for kernel_version in %{kernel_versions}; do

    install -D -m 755 _kmod_build_${kernel_version%%___*}/foo/foo.ko ${RPM_BUILD_ROOT}%{kmodinstdir_prefix}/foo.ko
done
chmod u+x ${RPM_BUILD_ROOT}/lib/modules/*/extra/*/*
  • But using "make install" should be preferred, as you that way will get a new module automatically should upstream suddenly add another one.

kmodtool

Kmodtool is a simple bash script that creates parts of the spec file dynamically depending on how you call it -- that's why the kmod template above has no files section. Kmodtool gets called like this

# kmodtool does its magic here

%{expand:%(kmodtool --target %{_target_cpu} --repo %{repo} --kmodname foo %{?buildforkernels:--%{buildforkernels}} %{?kernels:--for-kernels "%{?kernels}"} 2>/dev/null) }

Kmodtool itself is no real magic -- if you understand a bit of bash scripting you should be able to see how it works. It created three major parts: one that defines some macros, a meta-package kmod-foo per kernel variant and a package kmod-foo-<kernel-version> per kernel variant which contains the module itself and gets tracked in for new kernels by new kmod-foo meta package.

kmod-foo-<kernel-version> package

The most important part of kmodtool is the function print_rpmtemplate_per_kmodpkg, which contains a template for the package which will hold the kmod later:

%package       -n kmod-${kmodname}-${verrel}${variant}

Summary:          ${kmodname} kernel module(s) for ${verrel}${variant}

Group:            System Environment/Kernel

Provides:         kernel-modules-for-kernel = ${verrel}${variant}

Provides:         ${kmodname}-kmod = %{?epoch:%{epoch}:}%{version}-%{release}

Requires:         ${kmodname}-kmod-common >= %{?epoch:%{epoch}:}%{version}

Requires(post):   /sbin/depmod

Requires(postun): /sbin/depmod

Requires:         kernel-%{_target_cpu} = ${verrel}${variant}
BuildRequires:    kernel${dashvariant}-devel-%{_target_cpu} = ${verrel}


%post          -n kmod-${kmodname}-${verrel}${variant}

/sbin/depmod -aeF /boot/System.map-${verrel}${variant} ${verrel}${variant} > /dev/null || :

%postun        -n kmod-${kmodname}-${verrel}${variant}

/sbin/depmod  -aF /boot/System.map-${verrel}${variant} ${verrel}${variant} &> /dev/null || :

%description  -n kmod-${kmodname}-${verrel}${variant}

This package provides the ${kmodname} kernel modules built for the Linux

kernel ${verrel}${variant} for the %{_target_cpu} family of processors.


%files        -n kmod-${kmodname}-${verrel}${variant}

%defattr(644,root,root,755)

/lib/modules/${verrel}${variant}/extra/${kmodname}/

This macro later expands to something like the following when building for a standard kernel 2.6.23.1-42.fc8 on i686 and %{version} = 1.5; the output get inserted into the spec file before building :

%package       -n kmod-foo-2.6.23.1-42.fc8

Summary:          foo kernel module(s) for 2.6.23.1-42.fc8

Group:            System Environment/Kernel

Provides:         kernel-modules-for-kernel = 2.6.23.1-42.fc8

Provides:         foo-kmod = 1.5-1
Requires:         foo-kmod-common >= 1.5-1
Requires(post):   /sbin/depmod

Requires(postun): /sbin/depmod



Requires:         kernel-i686 = 2.6.23.1-42.fc8

BuildRequires:    kernel-devel- i686 = 2.6.23.1-42.fc8

%post          -n kmod-foo-2.6.23.1-42.fc8

/sbin/depmod -aeF /boot/System.map-2.6.23.1-42.fc8 2.6.23.1-42.fc8 > /dev/null || :

%postun        -n kmod-foo-2.6.23.1-42.fc8

/sbin/depmod  -aF /boot/System.map-2.6.23.1-42.fc8 2.6.23.1-42.fc8 &> /dev/null || :



%description  -n kmod-foo-2.6.23.1-42.fc8

This package provides the foo kernel modules built for the Linux

kernel 2.6.23.1-42.fc8 for the %{_target_cpu} family of processors.


%files        -n kmod-foo-2.6.23.1-42.fc8

%defattr(644,root,root,755)

/lib/modules/2.6.23.1-42.fc8/extra/foo/

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

  • %package       -n kmod-foo-2.6.23.1-42.fc8

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

  • Provides:         kernel-modules-for-kernel = 2.6.23.1-42.fc8

Via this provides depsolvers or scripts can check for which kernel a module was build.

  • 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.23.1-42.fc8

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.23.1-42.fc8

Needed for building kernel-modules. This is just here if we are building for a Fedora kernel and will not show up when building for a custom kernel

  • %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.23.1-42.fc8/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!

kmod-foo

This is a meta-package which only purpose is to depend on the latest kmod-foo-<kernel-version> package. Without this meta-pacakge the later would be tracked in automatically for newly released kernels, because yum can't know that kmod-ntfs-2.6.23.1-49.fc8.x86_64 is a update for kmod-ntfs-2.6.23.1-42.fc8.x86_64 and that the latter should remain installed.

The meta-package gets created in the function print_rpmtemplate_kmodmetapkg from kmodtool. It will create a package which looks like this:

%package      -n kmod-foo
Summary:         Metapackage which tracks in foo kernel module for newest kernel
Group:           System Environment/Kernel
Requires:        kmod-foo-2.6.23.1-49.fc8

%description  -n kmod-foo
This is a meta-package without payload which sole purpose is to require the
foo kernel module(s) for the newest kernel,
to make sure you get it together with a new kernel.

%files        -n kmod-foo
%defattr(644,root,root,755)

macros

Kmotool further creates three macros that can be used in the spec file during the %prep, %build and %install stages.

%define kmodinstdir_prefix  /lib/modules/
%define kmodinstdir_postfix  /extra/ntfs/
%define kernel_versions 2.6.23.1-49.fc8___%{_usrsrc}/kernels/2.6.23.1-49.fc8-%{_target_cpu} 2.6.23.1-49.fc8PAE___%{_usrsrc}/kernels/2.6.23.1-49.fc8PAE-%{_target_cpu}

Look above in the kmod spec file template for details how to use those three.

Anchor (V1vsV2)

kmods v1 versus v2

There were several design issues that lead to the the overhaul named kmods v2:

  • kmods v1 could only build for different variants ("" (UP/Standard), SMP, Xen, ...) of one kernel-version (say 2.6.21-1.1776_FC7) in one build cycle. That became hindering when Fedora introduced a dedicated kernel-xen package with a different EVR than the main kernel package. The new kmods version can build for different kernel-versions and variants easily and allows skipping certain variants (say for example xen) a lot easier.
  • FESCo explicitly wanted to builds kmods only the latest kernel versions when they designed kmods v1; but sometimes there are situations where a kmod for a older kernel might be handy or needed, thus the new kmods standard allows to build for old and new kernels during one build cycle as well. To make is poissble for users to get the kmods for older kernels the names need to be different, wich lead to slightly ugly package names for kmods, as they contaain the kernel-version now in in the name (e.g. kmod-foo-2.6.23.8-62.fc8-1.0-1, where kmod-foo-2.6.23.8-62.fc8 is the actually %{name}), but that solves some corner cases with dep-solvers as well.
  • the '--define "kversions foo" --define "kvariants bar"' systax when rebuilding kmods v1 was not very intuitive for people that wanted to rebuild kmods; '--define "kernels $(uname -r)"' is a lot easier to remember and can be scripted more easily
  • kmodtool and a big kmodtool specific header with a lot of macros was part of many packages directly, which was not easily to maintain and looked ugly. kmodtool is now in a separate package and only needed when it comes to building the kmods (beforehand it was needed needed to determine the BuildRequires as well)

Mini-FAQ

Is it possible to compile against self compiled or other kernels?

Yes, easily, it's just a few steps. If you don't have a rpm build environment set one up like this

$ su -c "yum -y install rpmdevtools kmodtool kernel-devel"
$ rpmdev-setuptree

Now download the kmod src.rpm to the local directory and rebuild it for the running kernel:

$ yumdownloader --source kmod-foo
$ rpmbuild --rebuild foo-kmod*.src.rpm --define "kernels $(uname -r)" --target $(uname -m)

At the end of the build output you will see the names of the RPMs rpmbuild built. Use yum to install it with a command like this

$ su -c "yum --nogpgcheck install ~/rpmbuild/RPMS/i686/kmod-foo-1.0-4.i686.rpm"

Some notes, as above commands need some adjustments often:

  • instead of foo insert the name of the kmod you want to rebuild
  • the package rpmdevtools installed in the first command contains the script rpmdev-setuptree, which creates a environment for building rpms in your home directory (~/.rpmmacros and ~/rpmbuild)
  • the package kmodtool installed in the first command is needed for building kmods
  • if you use a special kernel variant like xen or PAE you need to install kernel-PAE-devel or kernel-xen-devel instead of kernel-devel in the first command; these packages contain the files that are needed to build kernel modules for the kernels shipped by Fedora.
  • note that yum will install the latest version of the kernel-devel packages for the latest Fedora kernels; if you didn't update and restart your system often please make sure you are running a kernel that is matching the version of the kernel-devel package
  • if you want to build a kmod for a kernel you compiled yourself you may skip installing the kernel-devel package
  • note third command in the "[...]--rebuild foo-kmod*.src.rpm[...]" part contains a "*" -- normally that should match the package yumdownloader downloaded earlier, but if you have old versions around in the local directory be sure to just pick the latest one
  • instead of "$(uname -r)" you can use one or multiple kernel versions; e.g. "--define "kernels 2.6.23.1-42.fc8 2.6.23.1-42.fc8PAE 2.6.21-2949.fc8xen" will build three kmod packages
  • the "--target $(uname -m)" in the third command actually is only needed on x86-32 (i386) systems
  • the "--nogpgcheck" in the last command is needed, because the package that was just build is unsigned
  • using yum to install will make sure you get the package with the userland stuff (that contins the virtual provides "foo-kmod-common") from the repos.

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

Build the kmod (with and without mock) against a different kernel then the one that is running on your system.

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.

Do I need a special plugin for yum or other depsolvers?

It works fine without any plugins. Maybe a plugin will be written to fix two corner cases:

  • Case 1: if yum sees the newly releases kmods from a 3rd party repo for a newly released Fedora kernel before the Fedora mirror yum selected offers that new Fedora kernel; this leads to a "dependency not met" error by yum
  • Case 2: yum installs a newly released kernel but doesn't install the kmods for it, as they are not yet available in the repos or the mirror used; if the user reboots now the kmods will be missing, which can lead to problems as for example some popular graphic drivers will not work if the proper kernel modules are missing. Once the modules are available they can be easily installed by a simple "yum update".

Does it require special support in the buildsys of a repo?

No. There is just a minor glitch in some versions of mock that makes building for older kernels a two-cycle build (if you BuildRequire both kernel-devel-2.6.23.1-42.fc8 and kernel-devel-2.6.23.1-49.fc8 in a package mock/yum will only install kernel-devel-2.6.23.1-49.fc8)

This standard is stupid.

Maybe. Post a better one. No offense, more a FYI: FESCo and some people from Livna and RPM Fusion 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 get accepted. But technical advantages that are properly documented (and maybe even tested in real life) might convince us. But remember, the improvements need to be worth converting the existing packages over.

Why not simply use dkms

The short answer: Dkms especially for experienced users that often change kernels is a nice concept that also has a lot of usage cases for enterprise customers. Thus repos should consider to ship dkms packages in parallel to kmods. But for ordinary users shipping a pre-compiled module has many averages instead of doign a Gentoo-Like concept.

Examples

A good example for the kmod v2 stuff are the madiwfi/madwifi-kmod package from Livna; the ntfs packages are also a good starting point. For an example of the "skip the build for the xen kernel" take a look at the kmods for the graphic drivers from AMD and Nvidia.


CategoryPackaging CategoryKmodPackaging

Packaging/KernelModules/Kmods2 (last edited 2023-11-14 09:37:58 by anonymous)