The Linux kernel is subject to relentless development, and the developers feel an urge to improve kernel internals without worrying too much about backward compatibility. This kind of free development shows up in a number of incompatibilities between the device driver interface offered by different versions of the kernel. Nonetheless, no incompatibility is introduced at the application level, with the exception of those few applications whose task requires low-level interaction with kernel features (like ps).
The device driver, on the other hand, is directly linked to the kernel image and must therefore comply with any change in the data structures, global variables, and functions exported by the core system. During development, the internals are modified as new features are added or new implementations replace the old ones because they prove faster or cleaner. Although the incompatibilities require programmers to put in some extra work when writing a module, I see continuous development as a winning point of the Linux community: strict backward-compatibility eventually proves harmful.
This chapter describes the differences between 2.0.x and 2.1.43, which you can expect to be similar to the upcoming 2.2 release. Linus introduced the most relevant changes in the first few 2.1 versions, so that the kernel could go through several more 2.1 versions, giving driver writers time to stabilize things before the development is frozen to release a stable 2.2 version. The following sections describe how drivers can deal with differences between 2.0 and 2.1.43. I’ve modified all the sample code introduced throughout the book so that it compiles and runs with both 2.0 and 2.1.43, as well as most versions in between.
The new versions of the drivers are available in the
v2.1
directory in the online examples at the O’Reilly FTP
site. Compatibility between 2.0 and 2.1 is achieved by means of the
header file sysdep-2.1.h
, which can be incorporated in
your own modules. I chose not to span compatibility back to 1.2 to
avoid loading down the C code with too many conditionals, as
1.2-2.0 differences are already dealt with in the previous
chapters. As I’m closing this book, I am aware that other small
incompatibilities have been introduced since 2.1.43; I won’t comment
on them, though, because I can’t guarantee complete support for these
recent versions.
Note that in this chapter I won’t describe all the novelties introduced by the 2.1 development series. What I’m doing here is porting 2.0 modules to work with both 2.0 and 2.1 kernels. Exploiting 2.1 features would require dropping support for release 2.0, which doesn’t have such features. Version 2.0 remains the main focus of this book.
In writing sysdep-2.1.h
, I’ve tried to accustom you to
the new API, and the macros I’m introducing are used to make 2.1
code work with 2.0 instead of the reverse.
This chapter shows incompatibilies in decreasing order of importance; the most relevant differences are described first, while minor details are introduced later.
Modularization is becoming more and more important in the Linux
community, and the developers decided that a cleaner implementation
had to replace the old one. The header file <linux/module.h>
was completely rewritten in 2.1.18, and a new API was introduced. As
you might expect, the new implementation is easier to exploit than the
old one.
In order to load your modules, you’ll need the package
modutils-2.1.34 or newer (see Documentation/Changes
for
details). The package falls back on a compatibility mode when used
with older kernels, so you can replace modules-2.0.0 with the
new package even if you often switch between 2.0 and 2.1.
The new interface to symbol tables is far easier than the previous one and relies on the following macros:
EXPORT_NO_SYMBOLS;
This macro is the equivalent of
register_symtab(NULL);
. It can appear either inside
or outside a function, because it instructs the assembler
without generating real code. If you want to compile the
module under Linux 2.0, the macro should be used from within
init_module.
EXPORT_SYMTAB;
If you intend to export some symbols, the module must
define this macro before including
<linux/module.h>
.
EXPORT_SYMBOL(name);
This macro states that you want to export the symbol
name
. It must be used outside of any
function.
EXPORT_SYMBOL_NOVERS(name);
Using this macro instead of EXPORT_SYMBOL()
forces version information to be dropped, even when compiling code
with version support. This is useful for avoiding some unnecessary
recompilation. For example, the memset function will
always work the same way; exporting the symbol without
version information allows the developers to change the
implementation (and even the data types being used) without
insmod tagging the incompatibility. It’s very unlikely
you’ll need this macro in modularized code.
If neither of these macros is used in your source, all non-static symbols are exported; this is the same behavior that you get in 2.0. If the module is made up from multiple source files, you can export symbols from any of the sources while still being able to share any symbol within the module’s realm.
As you see, the new way to export symbol tables gets rid of a lot
of trouble, but the novelty introduces a major incompatibility: a
module that exports some symbols and wants to compile and run with
both 2.1 and 2.0 must use conditional compilation to include both
implementations. This is how the export module
(v2.1/misc-modules/export.c
) deals with the problem:
#ifdef __USE_OLD_SYMTAB__ static struct symbol_table export_syms = { #include <linux/symtab_begin.h> X(export_function), #include <linux/symtab_end.h> }; #else EXPORT_SYMBOL(export_function); #endif int init_module(void) { REGISTER_SYMTAB(&export_syms); return 0; }
The code above relies on the following lines of
sysdep-2.1.h
:
#if LINUX_VERSION_CODE < VERSION_CODE(2,1,18) # define __USE_OLD_SYMTAB__ # define EXPORT_NO_SYMBOLS register_symtab(NULL); # define REGISTER_SYMTAB(tab) register_symtab(tab) #else # define REGISTER_SYMTAB(tab) /* nothing */ #endif
When using 2.1.18 or newer, REGISTER_SYMTAB
expands to
nothing, as there’s nothing to do in init_module; using
EXPORT_SYMBOL
outside of any function is all that’s needed to
export the module’s symbols.
The new implementation of kernel modules exploits features of the ELF binary formats to achieve better flexibility. More specifically, when building ELF object files, you can declare sections other than ``text,'' ``data,'' and ``bss.'' A ``section'' is a contiguous data area, something similar to the concept of ``segment.''
As of 2.1, kernel modules must be compiled using the the ELF
binary formats. As a matter of fact, the 2.1 kernel exploits ELF
sections (see Section 17.10) and can only be compiled
in ELF. So the constraint for modules isn’t really a constraint. Using
ELF allows informational fields to be stored in the object file. The
curious reader can use objdump
--section-headers to look at section headers and
objdump --section=.modinfo
--full-contents to look at module-specific
information. The .modinfo
section, actually, is the one
used to store information about the modules, including which values
are considered ``parameters'' and can be modified at load time.
When compiling with 2.1, a parameter is declared as such by the macro:
MODULE_PARM(variable, type-description);
When you use the macro in your source file, the compiler is
intructed to insert a description string in the object file; such
a description states that variable
is a parameter and that its
type corresponds to type-description
. insmod and
modprobe look in the object file to ensure you are allowed
to modify variable
and to check the actual type of the
parameter. Type checking is an important feature for preventing
unpleasant errors, such as overwriting an integer value with a string or
mistaking long integers for short ones.
In my opinion, the best way to describe the macro is by showing a few lines of sample code. The code below belongs to an imaginary network card:
int io[4] = {0,}; /* io address and irq: at most 4 cards */ int irq[4] = {0,}; short verbose; /* allow extra messaging to debug problems */ char *options=NULL; /* textual options */ MODULE_PARM(io, "1-4i"); /* accept 1 to 4 integers */ MODULE_PARM(irq,"1-4i"); /* and the same for irq's */ MODULE_PARM(verbose, "h"); /* a short value */ MODULE_PARM(options, "s"); /* string */
The type-description
string is documented in full detail by
the header file <linux/module.h>
and can be found throughout
the kernel sources for your convenience.
One technique worth showing here is how to parameterize the length
of an array like io
above. For instance, suppose the number
of peripheral boards supported by the network driver is represented by
a MAX_DEVICES
macro instead of the hard-coded number 4. To
this aim, the header <linux/module.h>
defines a macro
(__MODULE_STRING
) that performs ``stringification'' of macros
using the C preprocessor. The macro can be used in the following way:
int io[MAX_DEVICES+1]={0,}; MODULE_PARM(io, "1-" __MODULE_STRING(MAX_DEVICES) "i");
In the previous line, the ``stringified'' value gets concatenated to the other strings to build the informational string in the object file.
The scull sample module declares its parameters
(scull_major
and other integer variables) as such using
MODULE_PARM
. This might be a problem when compiling with Linux
2.0, where the macro is undefined. The simple fix I chose is to define
MODULE_PARM
within sysdep-2.1.h
so that it expands to an
empty statement when compiling against 2.0 headers.
Other informational values can be stored in the
.modinfo section of the module, like
MODULE_AUTHOR()
, but they are currently unused. Refer to
<linux/module.h>
for more information.
The format of /proc/modules
changed slightly in 2.1.18, when
all the modularization code was rewritten. While this change doesn’t
affect source code, you might be interested in the details, as
/proc/modules
is often checked during module development.
The new format is line-oriented like the old one, and each line includes the following fields:
This field is the same as in Linux 2.0.
This is a decimal number reporting the length in bytes (instead of memory pages).
The count is reported as -1 if the module doesn’t have a usage count. This is a new feature introduced with the new modularization code; you can write a module whose removal is controlled by a function instead of a usage count. The function asserts whether the module can be unloaded or not. The ipv6 module, for example, uses this feature.
The flags are text strings, each of which is enclosed in parentheses, and they are separated by spaces.
The list as a whole is enclosed in brackets, and the individual names in the list are separated by spaces.
Here is how /proc/modules
might appear in a 2.1.43 system:
morgana% cat /proc/modules
ipv6 75164 -1
netlink 3180 0 [ipv6]
floppy 45960 1 (autoclean)
monitor 516 0 (unused)
In this screenshot, ipv6
has no usage count and relies on
netlink
; the floppy has been loaded by kerneld, as shown by the
``autoclean'' flag, and monitor
is a tiny tool of mine that
controls some status lights and turns off my computer at system
halt. As you can see by its being ``unused,'' I don’t care about its usage
count.
3.145.58.169