Migrating To Linux Kernel 2
Migrating To Linux Kernel 2
Migrating To Linux Kernel 2
The kernel is the heart of the Linux operating system, managing all system threads, processes, resources,
and resource allocation. Unlike most other operating systems, Linux enables users to reconfigure the kernel,
which is usually done to reduce its size and add or deactivate support for specific devices or subsystems.
Reconfiguring the kernel to remove support for unused devices and subsystems is quite common when
developing embedded systems, because a smaller kernel requires less memory, increasing the resources
available to your applications.
Device drivers are the software interface between your hardware and the Linux kernel. Device drivers are
low-level, hardware-specific software components that enable devices to interact with more generic, higher-
level application programming interfaces (APIs). Providing support for a specific subsystem or hardware
interface, such as SCSI, USB, or PCMCIA, is very different than providing support for every SCSI, USB, or
PCMCIA device. Testing every possible device that could be used over a specific subsystem is an
impossibility; new devices are being made available every day. The kernel provides support for specific
subsystems; device drivers provide support for specific devices that use those subsystems. Maintaining the
separation of high-level APIs and low-level device functionality makes it relatively fast and easy to add
support for new devices to existing systems by writing the appropriate driver for a new device and making it
available to the kernel.
Linux device drivers can be integrated into the kernel in two different ways: either by compiling them into
the kernel so that they are always available, or by compiling them into an object format that the kernel can
load whenever access to a specific device is required. Kernel code that can be automatically loaded into the
kernel is referred to as a loadable kernel module. When configuring the Linux kernel, each kernel
configuration editor displays a description of available kernel configuration variables and enables you to
specify whether each should be deactivated, compiled into the kernel, or compiled as a loadable kernel
module.
Compiling device drivers into the kernel has the advantage that they are always instantly available, but each
increases the size of the kernel that you are running. Compiling device drivers as loadable kernel modules
implies some slight overhead when you search for and initially load the module in order to access the
associated device, plus some small runtime overhead, but these are negligible compared to the savings in
size and associated memory requirements. Writing device drivers as loadable kernel modules also provides
significant advantages during development. As you develop and debug your device driver, you can
dynamically unload the previous version and load the new version each time you want to test the new
version. If your device driver is compiled into the kernel, you have to recompile the kernel and reboot each
time that you want to test a set of iterative changes. Similarly, developing and deploying device drivers as
loadable kernel modules simplifies maintaining them in the field, since the device driver can be updated as a
separate system component without requiring a kernel update.
Configuring your kernel for support for loadable modules is done in the Loadable module support section of
your kernel configuration editor. The Automatic kernel module loading option determines whether the kernel
will automatically try to locate and load modules as they are needed by new devices or subsystems. The
Module versioning support option (marked as experimental in the current 2.6 kernel source) adds extra
versioning information to compiled modules at build-time. This information is designed to help increase
module portability to kernels other than the one that they were compiled against. The Module unloading
option is new to 2.6 kernel support for loadable kernel modules. You must enable this option if you want
your kernel to be able to unload modules when they are no longer needed. This is especially important in
resource-constrained and power-sensitive environments such as embedded systems. If you activate this
option, you can also activate the Forced module unloading option, which enables you to forcibly unload
modules regardless of whether the kernel believes that they are in use.
Once you have compiled and installed a loadable kernel module (and you are using a kernel with Automatic
kernel module loading enabled), the loadable kernel module can be automatically loaded when the
corresponding device is first accessed, provided that the depmod command has been used to create the
module dependency tree. Loadable kernel modules are traditionally installed into a subdirectory of the
system's /lib/modules directory - the name of this subdirectory is based on the values of the VERSION,
PATCHLEVEL, SUBLEVEL, and EXTRAVERSION variables in the Makefile used to build your kernel.
The Linux 2.6 kernel introduces a new, unified framework for device drivers, which requires changes to
custom device drivers that you may have developed to run under earlier versions of the Linux kernel. The
new driver model provides a framework for full and complete support for device Plug and Play and power
management by defining the interfaces that these subsystems can use when communicating with individual
drivers. The new driver framework provides a much cleaner separation of responsibilities between buses and
drivers. The 2.6 Linux kernel also introduces the sysfs filesystem to provide a hierarchical view of each
system's device tree (and to prevent further overloading of the proc filesystem). The 2.6 Linux kernel also
introduces a new naming convention for loadable kernel modules, using the .ko extension (kernel object)
rather than the standard .o (object) extension used for loadable kernel modules in all previous stable
releases of the Linux kernel.
Explaining all of the nuances of converting a device driver to the 2.6 model is outside the scope of this white
paper, but is provided as a part of the documentation that accompanies the TimeSys Linux Development
Suite (LDS). This white paper highlights the major structural differences between device drivers under the
2.6 kernel and earlier versions of the Linux kernel.
A standard template for a proper device driver under the Linux 2.4 kernel is the following:
#define MODULE
#include linux/module.h>
#include linux/config.h>
#include linux/init.h>
Next, under the 2.6 kernel, the #define MODULE statement is no longer necessary, either in your code or in
your Makefile. This symbol definition and others are now automatically defined (if necessary) and verified for
you by the kernel build system, which you must now use when writing device drivers for the 2.6 kernel.
Using the kernel build system to produce external modules is discussed later in this white paper.
These are the basic structural changes that you need to make to an existing module to get it to compile and
load into a 2.6 kernel. However, once you load a module with this sort of structure, you will notice that an
error message about a tainted module is displayed on standard output and in the system log
(/var/log/messages). To eliminate this message, you need to add an instance of the MODULE_LICENSE()
macro, such as the following:
MODULE_LICENSE("GPL");
This macro, introduced in later versions of the 2.4 kernel, defines the module as being licensed under the
terms of GPL Version 2 or later. Other valid values are "GPL v2", "GPL and additional rights", "Dual
BSD/GPL" (your choice of BSD or GPL licensing), "Dual MPL/GPL" (your choice of Mozilla or GPL licensing),
and "Proprietary", which is self-explanatory.
A generic template for a minimal 2.6 device driver would therefore be the following:
#include img src="/files/misc/lt.gif">linux/module.h>
#include img src="/files/misc/lt.gif">linux/config.h>
#include img src="/files/misc/lt.gif">linux/init.h>
MODULE_LICENSE("GPL");
static int __init name_of_initialization_routine(void) {
/* code goes here */
return 0;
}
static void __exit name_of_cleanup_routine(void) {
/* code goes here */
}
module_init(name_of_initialization_routine);
module_exit(name_of_cleanup_routine);
Aside from changes required to device drivers themselves, the most significant change to working with
device drivers for the 2.6 Linux kernel are the changes in the build process described in the next section.
One basic change that affects everyone who works on a loadable device driver that is not part of the core
kernel source code is the integration of external module compilation into the standard kernel build
mechanism. If you are not using an integrated development environment such as TimeSys' TimeStorm that
is capable of detecting kernel versions and automatically creating Makefiles that "do the right thing" for the
2.6 kernel, you will need to manually create Makefiles for your device drivers that are integrated into the
kernel build process.
Under 2.4 and earlier Linux kernels, modules could be developed and compiled anywhere by passing
appropriate compilation flags on the command line or in the module's Makefile. These flags consisted of two
symbol definitions required when compiling modules, and a pointer to the directory containing the kernel
include files, as in the following example that would produce a loadable kernel module named testmod.o:
When building modules for the 2.6 kernel, the process is simpler, but the requirements for successful
compilation are different. By integrating the process of building external modules into the standard kernel
build system, you don't have to manually specify module-oriented declarations such as MODULE,
__KERNEL__, or newer symbols such as KBUILD_BASENAME and KBUILD_MODNAME. You also do not need
to specify options such as -O2 (an optimization option for the gcc C compiler that enables optimizations such
as inlining) - because your module is compiled like any other loadable kernel module, the make process
automatically uses all mandatory flags. Module Makefiles are therefore much simpler, as in the following
2.6-compatible Makefile for the module testmod.ko:
obj-m := testmod.o
However, in order to build an external module, you must have write access to your kernel source tree in
order to create temporary directories created and used during compilation. A sample command line for
building a module for the 2.6 kernel is the following, which would be executed from within the directory
containing your module's source code, as always:
This sample command assumes that your module source code and Makefile are located in the directory from
which you are excuting the command. If you are not using a POSIX shell such as BASH, you can replace the
SUBDIRS argument's use of the $PWD variable with SUBDIRS=`pwd`, which will use the actual pwd
command to identify the working directory.
This sample command would produce output like the following:
make: Entering directory `/usr/src/linux-2.6.1'
*** Warning: Overriding SUBDIRS on the command line can cause
*** inconsistencies
make[1]: `arch/i386/kernel/asm-offsets.s' is up to date.
Building modules, stage 2.
MODPOST
CC /home/wvh/timesys/2.6/testmod/testmod.mod.o
LD [M] /home/wvh/timesys/2.6/testmod/testmod.ko
make: Leaving directory `/usr/src/linux-2.6.1'
The compilation step (the line containing CC) produces the basic object file for the module. The subsequent
linker/loader step (the line containing LD) links in the file init/vermagic.o from the kernel source tree
identified on the command line, which integrates information about the kernel against which the module was
compiled and provides more stringent checks used when loading a module under 2.6. In general, the 2.6
module verification, loading, and unloading mechanisms are much more stringent than in previous versions
of the Linux kernel.
Successful completion of the make command produces the module testmod.ko ("kernel object"), using the
new kernel module naming convention. If you have modified your system's startup scripts to explicitly load
modules by name, you will need to make sure that they now take the new naming convention into account
when upgrading them to the 2.6 kernel, as discussed later in this series of white papers.
The 2.6 Linux kernel introduced many internal changes that require changes to existing drivers. Some of
these include changes to the kernel's asynchronous I/O mechanism, DMA support layer, memory and page
allocation mechanisms, the block device driver, a new generic disk interface, and many more. The functions
used for allocating and managing memory and pages have changed, using a new standard interface known
as mempool. Module reference counts, used to determine whether a module is in use and, if not, can safely
be unloaded, are managed and manipulated differently. Task queues have been replaced with work queues.
One significant change that affects a large number of different drivers is the new interface used for modules
that take parameters. The MODULE_PARM() macro has been replaced by explicit parameter declarations
made using the new module_param() macro.
The enhanced preemptibility and SMP-awareness of the 2.6 Linux kernel introduces some new concerns for
driver writers. In uniprocessor, non-preemptible Linux kernels, some drivers may assume that there is no
need to provide reentrancy between two processes since two processes could not be executing driver code
at the same. Drivers should therefore use a spinlock or mutex to protect data that could be accessed from
multiple processes. Considerations such as these are especially important when writing high-performance or
real-time device drivers for embedded environments such as TimeSys Linux.
Depending on the domain in which you are using the 2.6 kernel, other modifications to drivers may be
necessary. For example, though the devfs filesystem has been present in the kernel since later versions of
the 2.3 kernel and is marked as obsolete during 2.6 Linux kernel configuration, it is often used in domains
such as embedded computing, where devfs provides flexibility and a reduced /dev footprint. The devfs
filesystem is an intermediate step between the hardcoded device nodes used on earlier Linux and UNIX
systems, and the integration of udev, hotplug, and the sysfs filesystem. Support for udev is currently being
integrated into the standard 2.6 kernel, but is already available in commercial, reference 2.6 Linux
distributions from TimeSys. If you are using another Linux distribution, you may find that devfs support and
integration is attractive for the driver that you are working on.
If you want to use the devfs filesystem, you will need to explicitly activate support for it when building a
kernel, in the File systems -> Pseudo filesystems section of your kernel configuration editor. Using devfs
requires changes to how your device drivers identify any device nodes that they use. When using the
traditional /dev directory as the location of Linux device descriptor files, a device driver registers a device by
calling either the register_blkdev() or register_chrdev()functions, depending on whether the driver is
registering a block or character device, and must know its major and minor device numbers "in advance".
This is the case even if you are using the new udev device mechanism, since udev is primarily a hotplug-
aware set of scripts that automatically create and delete entries in the /dev directory.
When using the devfs device filesystem, device drivers must use the the devfs_register() system call to
register their devices. Drivers can either continue to use pre-assigned major and minor numbers, or they
can let devfs assign them automatically by specifying the DEVFS_FL_AUTO_DEVNUM flag to the
devfs_register()call.
Conclusion
Changes to the kernel are always motivated by improvements, whether new features, simplification, or
standardization. Each new Linux kernel introduces many changes that have implications for developers at all
levels. This white paper provided an overview of 2.6-related changes to device drivers and the build process.
Tools such as TimeSys TimeStorm that are already 2.6-aware provide updated driver templates and
automatically create and manage Makefiles for 2.6 loadable kernel modules. However, if you are manually
maintaining existing device drivers or developing new ones, you will need to incorporate 2.6-related changes
into your manual procedures.
For general information about writing device drivers, "Linux Device Drivers, 2nd Edition" by Alessandro
Rubini and Jonathan Corbet (ISBN 0596000081) is an excellent book on developing, debugging, and using
Linux device drivers. The version of this book associated with the 2.4 kernel is available online at the URL
http://www.xml.com/ldd/chapter/book/. A new version of this book is in progress to discuss 2.6 issues. Until
a new edition is available, Mr. Corbet has written an excellent series of articles on writing device drivers for
the 2.6 kernel. These are available at the Linux Weekly News. Similarly, the documentation provided with
TimeSys' Linux Development Suite (LDS) provides detailed examples of writing device drivers, focusing on
high-performance drivers for embedded use.