Automatic Loading

Generally, module loading is initiated from userspace, either by the user or by means of automated scripts. To achieve greater flexibility in the handling of modules and to improve transparency, the kernel itself is also able to request modules.

Where is the catch? It is not difficult for the kernel to insert the binary code once it has access to it. However, it cannot do this without further help from userspace. The binary file must be localized in the filesystem, and dependencies must be resolved. Because this is far easier to do in userspace than in kernel space, the kernel has an auxiliary task known as kmod to which these tasks are delegated. Note that kmod is not a permanent daemon, but is only initiated by the kernel on demand.

Let us examine a scenario that demonstrates the advantages of kernel-initiated module loading. It is assumed that the VFAT filesystem is available as a module only and is not permanently integrated into the kernel. If a user issues the following command for mounting a diskette:

[email protected]> mount -t vfat /dev/fd0 /mnt/floppy before the vfat module is loaded into the kernel, an error message would normally be returned indicating that the corresponding filesystem is not supported because it is not registered with the kernel. However, in practice this is not the case. The diskette is mounted without any problem, even if the module is not loaded. When the mount call terminates, the required modules are located in the kernel.

How is this possible? When the kernel processes the mount system call, it discovers that no information on the desired filesystem — vfat — is present in its data structures. It therefore attempts to load the corresponding module using the request_module function whose exact structure is discussed in Section 7.4.1. This function uses the kmod mechanism to start the modprobe tool, which then inserts the vfat module in the usual way. In other words, the kernel relies on an application in userspace that, in turn, uses kernel functions to add the module as illustrated in Figure 7-2.

Once this has been done, the kernel again tries to obtain information on the desired filesystem; as a result of the modprobe call, this information is now held in its data structures if, of course, the module actually exists — if not, the system call terminates with a corresponding error code.

Request by kernel

k Load module

Figure 7-2: Automatic module loading.

Figure 7-2: Automatic module loading.

request_module calls are located at various points throughout the kernel sources; with their help, the kernel attempts to make functions delegated to modules accessible as transparently as possible by adding the code automatically as needed and without user interaction.

Situations can arise in which it is not possible to uniquely define which module is required to provide the desired functionality. Consider the case that a USB stick is added to the system. The host controller driver recognizes the new device. The module that needs to be loaded is usb-storage, but how can the kernel know this? The solution to the problem is a small "database" that is attached to every module. The contents describe which devices are supported by the module. In case of USB devices, this is a list of supported interface types, manufacturer IDs, or any similar piece of information that identifies the device. Modules that provide a driver for PCI devices, as another example, also use the unique IDs associated with the device. The module provides a list of all supported devices.

The database information is provided via module aliases. These are generic identifiers for modules that encode the described pieces of information. The macro module_alias is used to generate module aliases.

<modules.h>

#define MODULE_INFO(tag, info) _MODULE_INFO(tag, tag, info)

#define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias)

<moduleparam.h>

#define _MODULE_INFO(tag, name, info) \

static const char _module_cat(name,_LINE_)[] \

_attribute_used__\

_attribute_((section(".modinfo"),unused)) = _stringify(tag) "=" info

The alias provided to module_alias is stored in the .modinfo section of the module binary. If a module provides several different services, appropriate aliases are inserted directly. The code for RAID 4, 5, and 6 is contained in the same module, for instance.

drivers/md/raid5.c

MODULE_ALIAS("md-personality-4"); /* RAID5 */

MODULE_ALIAS("md-raid5");

MODULE_ALIAS("md-raid4");

MODULE_ALIAS("md-level-5");

MODULE_ALIAS("md-level-4");

MODULE_ALIAS("md-personality-8"); /* RAID6 */

MODULE_ALIAS("md-raid6");

MODULE_ALIAS("md-level-6");

More important than direct aliases is the inclusion of device databases. The kernel provides the macro module_device_table to implement such databases. The device table for the 8139too module that was shown above is created by the following code:

drivers/net/8139too.c static struct pci_device_id rtl8139_pci_tbl[] = {

{0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },

{PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 },

MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl); The macro provides a standardized name in the module binary by which the table can be accessed: <module.h>

#define MODULE_GENERIC_TABLE(gtype,name) \

extern const struct gtype##_id _mod_##gtype##_table \

_attribute_ ((unused, alias(_stringify(name))))

#define MODULE_DEVICE_TABLE(type,name) \

MODULE_GENERIC_TABLE(type##_device,name)

In the case of PCI, this generates the ELF symbol_mod_pci_device_table, which is an alias for rtl8139_pci_tbl.

When modules are built, a conversion script (scripts/mod/file2alias.c) parses the device tables for the different bus systems (PCI, USB, IEEE1394, ... ) — which have all different formats — and generates module_alias entries for the database entries. This allows treating device databases entries in the same way as module aliases without having to duplicate the database information. Since the conversion process basically consists of parsing an ELF file and doing some string rewriting, I will not discuss it in greater detail here. The output looks as follows for the 813 9too module:

drivers/net/8139too.mod.c

MODULE_ALIAS("pci:v000010ECd00008139sv*sd*bc*sc*i*"); MODULE_ALIAS("pci:v000010ECd00008138sv*sd*bc*sc*i*"); MODULE_ALIAS("pci:v00001113d00001211sv*sd*bc*sc*i*");

MODULE_ALIAS("pci:v00001743d00008139sv*sd*bc*sc*i*"); MODULE_ALIAS("pci:v0000021Bd00008139sv*sd*bc*sc*i*"); MODULE_ALIAS("pci:v*d00008139sv000010ECsd00008139bc*sc*i*"); MODULE_ALIAS("pci:v*d00008139sv00001186sd00001300bc*sc*i*"); MODULE_ALIAS("pci:v*d00008139sv000013D1sd0000AB06bc*sc*i*");

Providing module aliases forms the basis to solve the automatic module loading problem, but is not yet completely sufficient. The kernel needs some support from userspace. After the kernel has noticed that it needs a module for a device with specific properties, it needs to pass an appropriate request to a userspace daemon. This daemon then seeks the apt module and inserts it into the kernel. Section 7.4 describes how this is implemented.

Continue reading here: Inserting and Deleting Modules

Was this article helpful?

0 0