Linux Device Drivers 2nd Edition (779877), страница 75
Текст из файла (страница 75)
Note the use of a constpointer for the data; it is assumed that it will be exported in a read-only mode.inter_module_r egister will complain (via printk) if the given string is alreadyregistered.When the data is no longer to be shared, the module should call inter_module_unr egister to clean it up:void inter_module_unregister(const char *string);Two functions are exported that can access data shared via inter_module_r egister :const void *inter_module_get(const char *string);This function looks up the given string and returns the associated datapointer.
If the string has not been registered, NULL is returned.31222 June 2001 16:40http://openlib.org.uaIntermodule Communicationconst void *inter_module_get_request(const char *string,const char *module);This function is like inter_module_get with the added feature that, if the givenstring is not found, it will call request_module with the given module nameand then will try again.Both functions also increment the usage count for the module that registered thedata. Thus, a pointer obtained with inter_module_get or inter_module_get_r equestwill remain valid until it is explicitly released. At least, the module that created thatpointer will not be unloaded during that time; it is still possible for the moduleitself to do something that will invalidate the pointer.When you are done with the pointer, you must release it so that the other module’s usage count will be decremented properly. A simple call tovoid inter_module_put(const char *string);will release the pointer, which should not be used after this call.In our sample master module, we call inter_module_get_r equest to cause the intermodule to be loaded and to obtain the two pointers.
The string is simply printed,and the function pointer is used to make a call from master into inter. The additional code in master looks like this:static const char *ime_string = NULL;static void master_test_inter();void master_test_inter(){void (*ime_func)();ime_string = inter_module_get_request("ime_string", "inter");if (ime_string)printk(KERN_INFO "master: got ime_string ’%s’\n", ime_string);elseprintk(KERN_INFO "master: inter_module_get failed");ime_func = inter_module_get("ime_function");if (ime_func) {(*ime_func)("master");inter_module_put("ime_function");}}void master_cleanup_module(void){if (ime_string)inter_module_put("ime_string");}Note that one of the calls to inter_module_ put is deferred until module cleanuptime.
This will cause the usage count of inter to be (at least) 1 until master isunloaded.31322 June 2001 16:40http://openlib.org.uaChapter 11: kmod and Advanced ModularizationThere are a few other worthwhile details to keep in mind when using the intermodule functions. First, they are available even in kernels that have been configured without support for loadable modules, so there is no need for a bunch of#ifdef lines to test for that case.
The namespace implemented by the intermodule communication functions is global, so names should be chosen with care orconflicts will result. Finally, intermodule data is stored in a simple linked list; performance will suffer if large numbers of lookups are made or many strings arestored. This facility is intended for light use, not as a general dictionary subsystem.Version Control in ModulesOne of the main problems with modules is their version dependency, which wasintroduced in Chapter 2.
The need to recompile the module against the headers ofeach kernel version being used can become a real pain when you run several custom modules, and recompiling is not even possible if you run a commercial module distributed in binary form.Fortunately, the kernel developers found a flexible way to deal with version problems. The idea is that a module is incompatible with a different kernel versiononly if the software interface offered by the kernel has changed. The softwareinterface, then, can be represented by a function prototype and the exact definition of all the data structures involved in the function call.
Finally, a CRCalgorithm* can be used to map all the information about the software interface to asingle 32-bit number.The issue of version dependencies is thus handled by mangling the name of eachsymbol exported by the kernel to include the checksum of all the informationrelated to that symbol. This information is obtained by parsing the header files andextracting the information from them. This facility is optional and can be enabledat compilation time. Modular kernels shipped by Linux distributors usually haveversioning support enabled.For example, the symbol printk is exported to modules as something likeprintk_R12345678 when version support is enabled, where 12345678 is thehexadecimal representation of the checksum of the software interface used by thefunction. When a module is loaded into the kernel, insmod (or modpr obe) canaccomplish its task only if the checksum added to each symbol in the kernelmatches the one added to the same symbol in the module.There are some limitations to this scheme.
A common source of surprises has beenloading a module compiled for SMP systems into a uniprocessor kernel, or vice* CRC means ‘‘cyclic redundancy check,’’ a way of generating a short, unique number froman arbitrary amount of data.31422 June 2001 16:40http://openlib.org.uaVersion Control in Modulesversa.
Because numerous inline functions (e.g., spinlock operations) and symbolsare defined differently for SMP kernels, it is important that modules and the kernelagree on whether they are built for SMP. Version 2.4 and recent 2.2 kernels throwan extra smp_ string onto each symbol when compiling for SMP to catch this particular case. There are still potential traps, however. Modules and the kernel candiffer in which version of the compiler was used to build them, which view ofmemory they take, which version of the processor they were built for, and more.The version support scheme can catch the most common problems, but it stillpays to be careful.But let’s see what happens in both the kernel and the module when version support is enabled:•In the kernel itself, the symbol is not modified.
The linking process happensin the usual way, and the symbol table of the vmlinux file looks the same asbefore.•The public symbol table is built using the versioned names, and this is whatappears in /pr oc/ksyms.•The module must be compiled using the mangled names, which appear in theobject files as undefined symbols.•The loading program (insmod) matches the undefined symbols in the modulewith the public symbols in the kernel, thus using the version information.Note that the kernel and the module must both agree on whether versioning is inuse. If one is built for versioned symbols and the other isn’t, insmod will refuse toload the module.Using Version Support in ModulesDriver writers must add some explicit support if their modules are to work withversioning.
Version control can be inserted in one of two places: in the makefile orin the source itself. Since the documentation of the modutils package describeshow to do it in the makefile, we’ll show you how to do it in the C source. Themaster module used to demonstrate how kmod works is able to support versionedsymbols. The capability is automatically enabled if the kernel used to compile themodule exploits version support.The main facility used to mangle symbol names is the header <linux/modversions.h>, which includes preprocessor definitions for all the public kernel symbols. This file is generated as part of the kernel compilation (actually, ‘‘makedepend’’) process; if your kernel has never been built, or is built without versionsupport, there will be little of interest inside.
<linux/modversions.h> must be31522 June 2001 16:40http://openlib.org.uaChapter 11: kmod and Advanced Modularizationincluded before any other header file, so place it first if you put it directly in yourdriver source. The usual technique, however, is to tell gcc to prepend the file witha compilation command like:gcc -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h...After the header is included, whenever the module uses a kernel symbol, the compiler sees the mangled version.To enable versioning in the module if it has been enabled in the kernel, we mustmake sure that CONFIG_MODVERSIONS has been defined in <linux/config.h>.
That header controls what features are enabled (compiled) in the currentkernel. Each CONFIG_ macro defined states that the corresponding option isactive.*The initial part of master.c, therefore, consists of the following lines:#include <linux/config.h> /* retrieve the CONFIG_* macros */#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)# define MODVERSIONS /* force it on */#endif#ifdef MODVERSIONS# include <linux/modversions.h>#endifWhen compiling the file against a versioned kernel, the symbol table in the objectfile refers to versioned symbols, which match the ones exported by the kernelitself. The following screendump shows the symbol names stored in master.o.
Inthe output of nm, T means ‘‘text,’’ D means ‘‘data,’’ and U means ‘‘undefined.’’ The‘‘undefined’’ tag denotes symbols that the object file references but doesn’t declare.0000003400000000000000000000003400000000T cleanup_modulet gcc2_compiled.T init_moduleT master_cleanup_moduleT master_init_moduleU printk_Rsmp_1b7d4074U request_module_Rsmp_27e4dc04morgana% fgrep ’printk’ /proc/ksymsc011b8b0 printk_Rsmp_1b7d4074Because the checksums added to the symbol names in master.o are derived fromthe entire prototypes of printk and request_module, the module is compatible witha wide range of kernel versions. If, however, the data structures related to eitherfunction get modified, insmod will refuse to load the module because of its incompatibility with the kernel.* The CONFIG_ macros are defined in <linux/autoconf.h>.
You should, however,include <linux/config.h> instead, because the latter is protected from double inclusion, and sources <linux/autoconf.h> internally.31622 June 2001 16:40http://openlib.org.uaVersion Control in ModulesExporting Versioned SymbolsThe one thing not covered by the previous discussion is what happens when amodule exports symbols to be used by other modules. If we rely on version information to achieve module portability, we’d like to be able to add a CRC code toour own symbols.