Chapter 17. Recent Developments

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

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.

Exporting Symbols

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.

Declaring Parameters

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.

/proc/modules

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:

The module’s name

This field is the same as in Linux 2.0.

The module’s size

This is a decimal number reporting the length in bytes (instead of memory pages).

The usage count for this module

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.

Optional flags

The flags are text strings, each of which is enclosed in parentheses, and they are separated by spaces.

A list of modules that reference this module

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.58.169