Linux STREAMS (LiS)


Demand loading LiS modules and drivers

The LiS demand load system supports both the old kerneld and the new kmod mechanisms for demand loading kernel modules.

The convention for LiS kernel loadable object files is:

  • Their name start with "streams-".
  • They are placed in /lib/modules/<kver>/misc/, where <kver> is the kernel version.

Unconfigured drivers and modules

An unconfigured driver or module is a driver or module that LiS does not have any special information about. Even in this case, you can take advantage of demand loading.

Modules

Module demand loading is simple. When a user does an I_PUSH requesting that module mymod be pushed onto a stream, LiS will check if mymod is already registered. If not, LiS asks the kernel to load streams-mymod.

A security related implication of this is that any user capable of doing an I_PUSH can provoke loading of any module that has the streams- prefix.

Demand loading also works when autopushing modules, but you should be aware that when a module is unloaded, is it silently removed from all autopush lists.

You can have more than one module in a single kernel loadable module, but for demand loading to work with modules whose object names are not derived from the module name, you have to set up alias lines in /etc/conf.modules.
For example, if you have modules mymod and mymod2 in the kernel loadable object file streams-mymod.o, you have to add the line

alias streams-mymod2 streams-mymod

in your /etc/conf.modules for demand loading to work properly when pushing mymod2.

Drivers

Demand loading drivers is slightly more complicated. When a device special node with major device number major is opened, the kernel checks if device major is registered in the kernel. If not, the kernel will try to load "char-major-major".


If a clone open is done LiS will check if the driver is registered. If not, LiS will ask the kernel to load char-major-minor, where minor is the minor device number of the clone device opened. In this case the value of minor is the major device number of the driver that needs to be loaded and opened.

Kernel loadable modules generally do not have names of the form char-major-number, so a mapping to the correct name is needed. This is done with alias lines in /etc/conf.modules, or, on newer systems, /etc/modules.conf. If your driver has major device number 123 and is in the kernel loadable object file streams-mydriver.o, you should have the line

alias char-major-123 streams-mydriver

in your /etc/conf.modules.


If you also have the line

alias char-major-240 streams

in your /etc/conf.modules and the clone driver major is 240, both LiS and your driver will be demand loaded when you do a clone open of your driver.

You can configure autopush on an unconfigured driver, but you should be aware that when the driver is unloaded, all its autopush lists are silently removed.

You can have more than one driver in a single kernel loadable module, but you should set up a separate alias line for each driver.

Configured drivers and modules

When drivers and modules are added to the LiS configuration files, you can specify the object name that will be used for requesting a demand load of the driver or module. If demand loading of the configured object name fails, LiS will revert to the procedure for demand loading an unconfigured driver or module.


The advantage of configured drivers and modules is that the LiS installation procedure takes care of adding the needed alias lines to /etc/conf.modules. Also, the autopush configuration is not changed when a configured driver or module is unloaded.

Writing kernel loadable modules for LiS

It takes little extra effort to make LiS modules and drivers loadable. In most cases, all you have to do is to add the special Linux module functions init_module() and cleanup_module() and some lines to maintain the usage count of the loadable module.

The init_module() function id used for initializing the loadable module, and can be as simple as:    

  int init_module(void)   
  {          
    return lis_register_strmod(&mymod_info, "mymod");  
  }        

for a module, or    

  int init_module(void)
  {
    return lis_register_strdev(123,&mydrv_info, 10, "mydrv");
  }

for a driver.

In this example, the driver's major device number is assumed to be 123. You must know this major device number ahead of time so that you can make the entry in the /dev directory that will cause your driver to autoload. The major number in your /dev entry and in your call to lis_register_strdev must match.

The cleanup_module() function is called by the kernel just before a loadable module is unloaded. This function has to unregister the drivers and/or modules registered in the init_module() function.


The cleanup_module() function can be as simple as:
  void cleanup_module(void)
  {
      lis_unregister_strmod(&mymod_info);
  }
for a module, or
  void cleanup_module(void)
  {
      lis_unregister_strdev(123);
  }
for a driver.

To avoid that the loadable module is unloaded while somebody is still using it, a usage count has to be maintained. This is done using the macros MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT. MOD_INC_USE_COUNT is used in the open routines, but be careful not to call it if your open fails. MOD_DEC_USE_COUNT is used in the close routines.

For a complete example, see the ldl driver in drivers/str/linux/ldl.c.