Migrating to Linux
kernel 2.6 -- Part 2: Migrating device drivers to Linux kernel 2.6
by William von Hagen (Feb.
13, 2004)
Foreword
-- This whitepaper is the second in a series from TimeSys’s "2.6
Linux Resource Center" on using the new Linux 2.6 kernel.
Authored by TimeSys Senior Product Manager William von Hagen, the
whitepapers in this series place special emphasis on the primary
issues in migrating existing drivers, applications, and embedded
Linux deployments to a Linux distribution based on the 2.6 kernel.
Material presented is largely vendor-neutral.
Enjoy! .
. .
Overview of Device Drivers and Loadable Kernel Modules
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.
Updating the Basic Structure of a Device Driver
A
standard template for a proper device driver under the Linux 2.4
kernel is the following:
#define MODULE #includelinux/module.h> #include
linux/config.h> #include
linux/init.h> static int __init name_of_initialization_routine(void) { /* * code here */ } static void __exit name_of_cleanup_routine(void) { /* * code here */ } module_init(name_of_initialization_routine); module_exit(name_of_cleanup_routine);
One common problem with many 2.4 and
earlier device drivers is that they made assumptions about the names
of the module initialization and cleanup functions. When writing
device drivers for 2.4 and earlier Linux kernels, you did not have to
register the names of the module initialization and cleanup/exit
functions if you used the default names init_module() and
cleanup_module() for these functions. This was always "wrong",
but it is no longer acceptable. Under the 2.6 kernel, you must use
the module_init() and module_exit() macros to register the names of
your initialization and exit routines. These declarations are only
strictly necessary if you ever intend to compile the specified module
into the kernel, but it's always best to prepare for that
case.
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:
#includeimg 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.
Changes to the Build
Process for Modules
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:gcc -D__KERNEL__ -DMODULE
-I/usr/src/linux-2.4.21/include -O2 -c testmod.c
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:# make -C /usr/src/linux-2.6.1
SUBDIRS=$PWD modules
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.
Highlights of Other 2.6-Related Changes
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.
Other Considerations
When Updating Drivers
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.
About
the author: William von Hagen is a Senior Product Manager at
TimeSys Corp., has been a Unix devotee for over twenty years, and has
been a Linux fanatic since the early 1990s. He has worked as a system
administrator, writer, developer, systems programmer, drummer, and
product and content manager. Bill is the author of Linux
Filesystems, Hacking the TiVo, SGML for Dummies,
Installing Red Hat Linux 7, and is the coauthor of The
Definitive Guide to GCC (with Kurt Wall) and The Mac OS X
Power Users Guide (with Brian Profitt). Linux Filesystems
is available in English, Spanish, Polish, and traditional Chinese.
Bill has also written for publications including Linux
Magazine, Mac Tech, Linux Format,
and online sites such as Linux Planet and Linux
Today. An avid computer collector specializing in
workstations, he owns more than 200 computer systems.
Next?
The
next segment of this white paper series provides an overview of the
process of moving an existing desktop system to the 2.6 kernel. This
white paper will highlight other software requirements imposed by the
new kernel and administrative changes that you must make when
migrating an existing system to the 2.6 kernel.
For
additional whitepapers and other related information, visit TimeSys.
TimeSys also offers Webinars
on 2.6-related topics.
This article is part 2 of a
series of whitepapers by von Hagen on Migrating to Linux kernel
2.6. The series includes:
Using the 2.6 Kernel with Your Current System (coming soon)
Migrating custom Linux installations to 2.6 (coming soon)
Migrating applications to the 2.6 kernel and NPTL (coming soon)