How to Build Kernel Modules

Currently maintained by: AlanBartlett and AkemiYagi

Please note that if you make changes to your kernel, you may no longer get support or help from the CentOS development team. The procedures described here are not officially endorsed by CentOS. This tutorial is provided in a hope that it will be useful when building your own kernel modules.

There are occasions when you need to modify a kernel module or create a new one. This may be to add certain features or simply to apply a patch. In this tutorial, we will attempt to apply a bug fix to an existing module and then install it.

It is assumed that you already have the full kernel source tree installed. If you followed Section 2 of I need the Kernel Source, it will be found in the directory ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18.`uname -m`/. Throughout this tutorial a system with 32-bit architecture is assumed. Hence the string returned by `uname -m` is i686 and the directory referenced above will be ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18.i686/.

<!> You are strongly advised against performing module building as root. (See: Building Source RPM as non-root under CentOS)

1. Building a kernel module (*.ko)

Let us assume, as an example, you are applying a patch to the cifs module which is located in the ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18.i686/fs/cifs/ directory.

1. Apply the patch(es) to the source file(s) as required.

2. Change to the root directory of the kernel source and, if this is the first time the kernel has been compiled, configure the kernel remembering to ensure that the relevant component of the kernel is set, within its configuration file (.conf), to be built as a module.

[user@host]$ cd ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18.i686
[user@host linux-2.6.18.i686]$ make oldconfig
[user@host linux-2.6.18.i686]$ make menuconfig
[user@host linux-2.6.18.i686]$ make prepare

3. Create the files required for compiling external modules.

[user@host linux-2.6.18.i686]$ make modules_prepare

Note: make modules_prepare will not build a Module.symvers file even if the parameter CONFIG_MODULEVERSIONING has been set. Therefore a full kernel build will have to have been executed previously to make module versioning work. For more details, see Sections #2.4 & #7 of /usr/share/doc/kernel-doc-2.6.18/Documentation/kbuild/modules.txt

4. Compile the module by specifying the relative path to the module’s Makefile and source code.

[user@host linux-2.6.18.i686]$ make M=fs/cifs

Note: The whole directory containing the module can be located anywhere. If, for example, it is in ~/mycifs/ run the following command making sure that you are still in the root directory of the kernel source.

[user@host linux-2.6.18.i686]$ make M=~/mycifs

5. In this example, the file cifs.ko has just been created. As root, copy the .ko file to the /lib/modules/<kernel-version>/extra/ directory.

[root@host linux-2.6.18.i686]# cp fs/cifs/cifs.ko /lib/modules/`uname -r`/extra

For further details of the directories into which modules are installed, see Section #6 of /usr/share/doc/kernel-doc-2.6.18/Documentation/kbuild/modules.txt

6. As root, run the depmod command to update the module dependency files.

[root@host linux-2.6.18.i686]# depmod -a

7. A further few steps are required to build the different modules for the different types of kernel. Edit the main Makefile in the root directory of the kernel source. For CentOS 5, find:

EXTRAVERSION = -prep

and replace it with (as appropriate):

EXTRAVERSION = -92.1.6.el5
EXTRAVERSION = -92.1.6.el5PAE
EXTRAVERSION = -92.1.6.el5xen
EXTRAVERSION = -92.1.6.el5.centos.plus
EXTRAVERSION = -92.1.6.el5.centos.plusPAE
EXTRAVERSION = -92.1.6.el5.centos.plusxen

The number is not very important if you are going to use modprobe to install the module. (A -92.el5 module will install into a -92.1.6.el5 kernel, etc.). However the PAE and xen modifiers are required to be used for those types of kernels.

Hint: After compiling the module for the standard kernel, edit the EXTRAVERSION line to add PAE or xen and run make modules_prepare. Then run make M=xxx to compile the module for the kernel version specified on the EXTRAVERSION line.

2. Building a kernel module using Dynamic Kernel Module Support (DKMS)

The method described above builds a module for a particular kernel version. If you upgrade the kernel or change the hardware architecture, you will have to manually build the module once again. The dynamic kernel module support (DKMS) framework is basically a duplicate tree, outside of the kernel source, that holds the source and compiled binaries for a particular module. DKMS can be called on to build, install or uninstall modules. DKMS requires the module source code to be located on your system. The DKMS binary takes care of building and installing the module(s) into any kernel(s) you may have on your system.

Here we will use the same example as above and build & install the cifs module. You will need to assume root's powers for this entire section.

1. Install the kernel-devel package that matches your current kernel.

2. Install the dkms package from the Repositories/RPMForge repo.

3. Create a directory /usr/src/<module>-<module-version>/

[root@host]# mkdir /usr/src/cifs-1.45fixed/

4. Copy the module's source code to that directory.

[root@host]# cd ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18-i686/fs/cifs
[root@host cifs]# cp -a * /usr/src/cifs-1.45fixed/

5. Create a dkms.conf file in the directory /usr/src/<module>-<module-version>/

[root@host cifs]# cd /usr/src/cifs-1.45fixed
[root@host cifs-1.45fixed]# vi dkms.conf

The dkms.conf file will need to contain these lines:

PACKAGE_NAME="cifs"
PACKAGE_VERSION="1.45fixed"
BUILT_MODULE_NAME[0]="cifs"
DEST_MODULE_LOCATION[0]="/kernel/fs/cifs/"
AUTOINSTALL="yes"

Note: The DEST_MODULE_LOCATION[0] line will be ignored during module installation because this will always be to the /lib/modules/<kernel-version>/extra/ directory. This parameter, however, will be used when a module is uninstalled as the location to which the stored, old module (if any) will be restored.

6. Add the <module>/<module-version> to the DKMS tree.

[root@host cifs-1.45fixed]# dkms add -m cifs -v 1.45fixed

7. Compile the module, under DKMS control.

[root@host cifs-1.45fixed]# dkms build -m cifs -v 1.45fixed

8. Install the module, again under DKMS control.

[root@host cifs-1.45fixed]# dkms install -m cifs -v 1.45fixed

Other DKMS actions to note are uninstall, remove, status and mkrpm which uninstalls the module from the kernel, removes the <module>/<module-version> from the DKMS tree, displays the current DKMS status of the system and creates a rpm file in the directory /var/lib/dkms/<module>/<module-version>/rpm/ respectively.

See also:

3. Building a kernel module rpm package (kmod)

You can also create rpm files for kernel modules. The kernel module rpm package (kmod) may then be installed by using the rpm command just like any other package. However, rpm packaging of kernel modules is handled differently from the standard packaging process. The example, below, provides the basic kernel module rpm packaging technique.

Once again we will use the cifs module as an example and create a set of kmod-cifs rpm files.

1. Install all the kernel-devel packages for your current kernel.

2. The patched source code is in the ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18.i686/fs/cifs/ directory as in Section 1, above. Make a working copy of that directory.

[user@host]$ mkdir ~/cifs-1.45/
[user@host]$ cd ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18.i686/fs/cifs
[user@host]$ cp -a * ~/cifs-1.45/

3. Create a Kbuild file by copying the Makefile.

[user@host]$ cd ~/cifs-1.45
[user@host]$ cp Makefile Kbuild

4. Create a bzip2 compressed tarball containing the source directory.

[user@host]$ cd ..
[user@host]$ tar -cf cifs-1.45.tar cifs-1.45/
[user@host]$ bzip2 cifs-1.45.tar

5. Copy the compressed tarball to the SOURCES directory.

[user@host]$ cp cifs-1.45.tar.bz2 ~/rpmbuild/SOURCES/

6. Make a copy the script kmodtool (part of the redhat-rpm-config package) in the SOURCES directory.

[user@host]$ cd ~/rpmbuild/SOURCES/
[user@host SOURCES]$ cp /usr/lib/rpm/redhat/kmodtool kmodtool-cifs

7. Edit the kmodtool-cifs file to ensure that the line referencing kmod-common is commented out and to add any requirements (if any) the package may need to have satisfied.

[user@host SOURCES]$ vi kmodtool-cifs

#
# RHEL5 - Remove common package requirement on general kmod packages.
# Requires: ${kmod_name}-kmod-common >= %{?epoch:%{epoch}:}%{version}
#
    cat <<EOF
Requires(post):   /sbin/depmod
Requires(postun): /sbin/depmod
# Uncomment and edit the next line, if required.
# Requires:   your_requirements >= x.y.z
EOF

8. Create a cifs-kmod.spec file in the SPECS directory.

[user@host SOURCES]$ cd ~/rpmbuild/SPECS/
[user@host SPECS]$ vi cifs-kmod.spec

# Sources:
Source0:  cifs-1.45.tar.bz2
Source10: kmodtool-cifs
#
# If kversion isn't defined on the rpmbuild line, build against the current kernel.
%{!?kversion: %define kversion %(uname -r)}
#
%define kmod_name cifs
%define kmodtool sh %{SOURCE10}
%define kverrel %(%{kmodtool} verrel %{?kversion} 2>/dev/null)
%define kmodrel %(echo %{kverrel} | sed 's/2.6.18-//')
#
%define basevar ""
%ifarch i686
%define paevar PAE
%endif
%ifarch i686 x86_64
%define xenvar xen
%endif
#
%{!?kvariants: %define kvariants %{?basevar} %{?xenvar} %{?paevar}}
#
Name:       %{kmod_name}-kmod
Version:    1.45fixed
Release:    1.%{kmodrel}
Summary:    cifs 1.45 CentOS 5 kmod (bug fix)
License:    GPL v2
Group:      System Environment/Kernel
URL:        http://www.centos.org/
#
BuildRoot:     %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
ExclusiveArch: i686 x86_64
#
%description
This package provides a cifs kernel module with a bug fix (CentOS bug #1776).
It is built to depend upon the specific ABI provided by a range of releases
of the same variant of the CentOS kernel and not on any one specific build.
#
# Magic hidden here:
%define kmp_version %{version}
%define kmp_release %{release}
%{expand:%(%{kmodtool} rpmtemplate_kmp %{kmod_name} %{kverrel} %{kvariants} 2>/dev/null)}
#
%prep
%setup -q -c -T -a 0
for kvariant in %{kvariants} ; do
    cp -a cifs-1.45 _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}" modules M=$PWD
    popd
done
#
%install
export INSTALL_MOD_PATH=$RPM_BUILD_ROOT
export INSTALL_MOD_DIR=extra/%{kmod_name}
for kvariant in %{kvariants} ; do
    ksrc=%{_usrsrc}/kernels/%{kverrel}${kvariant:+-$kvariant}-%{_target_cpu}
    pushd _kmod_build_$kvariant
    make -C "${ksrc}" modules_install M=$PWD
    popd
done
#
%clean
rm -rf $RPM_BUILD_ROOT
#
%changelog
* Fri May 18 2007 Akemi Yagi <amyagi@gmail.com>
- Example spec file.

9. Build the package.

[user@host SPECS]$ rpmbuild -bb --target=`uname -m` cifs-kmod.spec 2> build-err.log | tee build-out.log

If you do not wish to build the kmod packages for your currently running kernel, you can specify the the kernel version on the command line. For example:

[user@host SPECS]$ rpmbuild -bb --target=`uname -m` --define 'kversion 2.6.18-92.el5' cifs-kmod.spec 2> build-err.log | tee build-out.log

will build the kmod packages for the 2.6.18-92.el5 kernel.

In a similar fashion, you may select which kernel-variant kmod package(s) to build. For example:

[user@host SPECS]$ rpmbuild -bb --target=`uname -m` --define 'kvariants ""' cifs-kmod.spec 2> build-err.log | tee build-out.log

will build just the base-kernel kmod package.

When the build completes, there will be a set of kmod-cifs rpm files in the ~/rpmbuild/RPMS/`uname -m`/ directory.

See also:

HowTos/BuildingKernelModules (last edited 2008-07-17 16:59:51 by AlanBartlett)