Kernel module initialization refers to the tasks necessary to incorporate
a kernel module into the kernel and make it available for use by the system.
After you write your kernel module, you create a single binary image (a file
with the
.mod
extension) from the kernel module source
file (usually a C file).
This file is loaded into memory and its
configure
routine is called to perform initialization.
Module initialization
consists primarily of allocating and initializing data structures and calling
on other kernel modules to tell them that your module is loaded and available.
The
configure
routine manages initialization.
This
chapter describes how this routine performs a variety of initialization tasks,
including:
Initializing the kernel module at system startup or at run time
Preparing the kernel module for removal from the system
Other requests to the
configure
routine, such as
reconfiguring the kernel module when an attribute value changes and returning
information from the attribute table, are covered in
Chapter 3.
2.1 The configure Routine
The
configure
routine handles requests targeted at
the kernel module and performs the required actions.
The
configure
routine's structure is the same
for all kernel modules, regardless of the function they perform and whether
or not the kernel module is a device driver.
The naming convention for the
configure
routine
requires that the name of the routine be the module name followed by
_configure
.
This allows the module framework to locate the routine
and call it.
For example, for the kernel module
example.mod
,
the
configure
routine would be named
example_configure
.
Note that if your module does not contain a properly named
configure
routine, one of the following conditions will occur:
For statically loaded modules, the
/vmunix
kernel will not be able to build.
For dynamically loaded modules, the module will not be able to load into memory.
The
configure
routine accepts the following parameters:
op (cfg_op_t)
The module framework sets this parameter to one of several request codes that describe the operation the module should perform:
Initialize the module -
CFG_OP_CONFIGURE
Obtain attribute values -
CFG_OP_QUERY
Change attribute values -
CFG_OP_RECONFIGURE
Prepare the module for unloading -
CFG_OP_UNCONFIGURE
These operations are described in Section 2.1.2.
indata (cfg_attr_t)
Specifies a pointer to an array of data structures that contain information about the attributes in your kernel module attribute table, plus status information. The module framework checks the validity of attribute values when it copies attributes into memory, and it sets the status to indicate whether the value passes those tests.
indatalen (size_t)
Specifies the number of structures
in the
indata
array.
outdata (cfg_attr_t)
Specifies a pointer to a module-specific
output data structure when the
op
parameter specifies a
subsystem-defined operation.
Otherwise, its value is
NULL
.
outdatalen (size_t)
Specifies the size of the
outdata
parameter in bytes.
Typically, the
configure
routine is written as a
switch
statement, with one
case
statement to
handle each operation.
For example:
int example_configure (cfg_op_t op, cfg_attr_t *indata, size_t indatalen, cfg_attr_t *outdata, size_t outdatalen) { int status;.
.
.
.
switch(op) { case CFG_OP_CONFIGURE: status=value;
.
.
.
break; case CFG_OP_QUERY: status=value;
.
.
.
break; case CFG_OP_UNCONFIGURE: status=value;
.
.
.
break; case CFG_OP_RECONFIGURE: status=value;
.
.
.
break; default: status=ENOTSUP; break; } return (status); }
The
ENOTSUP
error return value indicates that the
kernel module does not support the requested operation.
Otherwise, the routine
returns a status value appropriate for the request.
(See
Section 2.1.3
for information on return status values.)
The
configure
routine accepts several parameters
(see
Section 2.1.1
for a list of all parameters accepted by
the
configure
routine).
The
op
parameter
takes one of the following request codes, which describe the specific operation
the module should perform:
CFG_OP_CONFIGURE
When the module framework calls the
configure
routine
with the
CFG_OP_CONFIGURE
request code, the kernel module
begins initialization.
In this way, the
configure
routine
functions similarly to the
main()
routine in a user program.
Your kernel module must be initialized whether it is loaded dynamically or
statically.
Section 2.2.1
describes this operation in more detail.
CGF_OP_QUERY
This request code retrieves values of attributes defined in the module attribute table. The kernel module should initialize the values of attributes stored in the module attribute table so that the proper values are retrieved. (See Chapter 3 for more information.)
CFG_OP_RECONFIGURE
This request code specifies that values for some attributes in the module attribute table have been set and that the kernel module should operate based on changes to the values of the attributes. (See Chapter 3 for more information.)
CFG_OP_UNCONFIGURE
This request code specifies that an attempt to unload your module has
been requested, which will result in either module cleanup or a return error.
In effect, this request asks your module to undo the initialization tasks
performed in
CFG_OP_CONFIGURE
and prepare it for removal
from the system.
(See
Section 2.2.2
for more information.)
The
configure
routine may return any standard status
value from the file
/usr/include/errno.h
as an
int
to the module framework.
The following list defines the most
common return status values:
ESUCCESS
- Indicates success.
ENOMEM
- Indicates insufficient memory.
ENOTSUP
- Indicates that the operation
is not supported.
ENOSYS
- Indicates that the operation
is not supported at this time.
It may have been called too early and is supported
later in the boot timeline.
EINVAL
- Indicates that an unrecognized
parameter was passed (for example,
indata
,
indatalen
).
The return status value is later appended to the higher 16 bits of a
final return that is returned to the caller.
The module framework status resides
in the lower 16 bits of the return status.
2.2 Module Initialization
Before a kernel module can be useful, it typically needs to initialize
data structures and let other kernel modules know that it exists and is available.
The module framework calls the
configure
routine with the
CFG_OP_CONFIGURE
request code to alert the module to perform initialization.
Likewise, the module framework passes the
CFG_OP_UNCONFIGURE
request code to alert the kernel module to prepare for removal from the system.
These codes are described in detail in the following sections.
2.2.1 Receiving the CFG_OP_CONFIGURE Request
The module framework calls the
configure
routine
with the
CFG_OP_CONFIGURE
request code to request that
the module perform its one-time initialization.
This is always the first call
into the module, regardless of whether it is statically or dynamically loaded.
If the kernel module is statically loaded, the module framework calls the
configure
routine very early in the boot timeline.
Because of this,
the kernel module typically registers callback routines to execute immediately
or at specific dispatch points to perform initialization tasks.
These tasks
include:
Allocating data structures
Initializing locks
Starting kernel threads
Registering with other subsystems
When you code your kernel module initialization process using callbacks,
the result is a single binary image that can be loaded statically or dynamically.
Otherwise, your kernel module will be either a static module or a dynamic
module, but not both.
Chapter 4
expands on this concept
by discussing the relationship between callbacks and dispatch points.
The
following sections present further considerations for modules that are loaded
either statically or dynamically.
2.2.1.1 Implementing Statically or Dynamically Loaded Kernel Modules
When a kernel module is statically loaded, it is linked as part of
/vmunix
and loaded into memory as part of the kernel.
The module
framework must call the
configure
routine with the
CFG_OP_CONFIGURE
request code before memory can be allocated, locks
can be used, and subsystems can be used.
As a result, a statically loaded
module typically is not able to perform initialization when its
configure
routine is called with the
CFG_OP_CONFIGURE
request code.
Instead, it registers callbacks that are invoked when these
resources become available as the system boots.
In contrast, a dynamically
loaded module is linked as its own image and loaded into memory on its own.
If you used callbacks in a dynamically loaded module, the initialization still
occurs properly.
To overcome the problem of resources not being available for a statically
loaded module, the
configure
routine registers callback
routines to be called at specific dispatch points, as described in
Chapter 4.
Initialization takes place when these callback routines are called.
Callbacks
enable your module to be a single binary image that can be statically or dynamically
loaded.
2.2.1.2 Checking the Configuration
To handle initialization correctly, whether your module is statically loaded or dynamically loaded, global variables keep track of the following information:
Whether the kernel module has already been initialized
A kernel module receives the
CFG_OP_CONFIGURE
request
code only once.
Therefore, you define a global variable to keep track of
this information and set the variable's initial value to
FALSE
.
When the
configure
routine successfully accepts the
CFG_OP_CONFIGURE
request, set this value to
TRUE
.
For example, for a kernel module named
example.mod
, the
module defines the
example_config
global variable as follows
to indicate whether the module has been initialized:
int example_init_config = FALSE;
Whether the module was dynamically or statically loaded
The module framework returns the current configuration state when you
call the
cfgmgr_get_state
routine.
The
cfgmgr_get_state
routine returns
SUBSYSTEM_STATICALLY_CONFIGURED
if the module was statically loaded.
It returns
SUBSYSTEM_DYNAMICALLY_CONFIGURED
if the module was dynamically loaded.
Your module can call this
routine if it needs to know how it was loaded.
Typically, a kernel module
should be written such that it does not need to call this routine.
Whether the module's callback routines completed successfully
When the kernel module is configured at startup, callback routines run at different times along the boot timeline. Therefore, global variables are the only way to communicate the success or failure of the callback routines. For example, you would not want to perform any postconfiguration operations if the preconfiguration callback routine failed.
In this example, the following global variable is defined to hold the callback status:
int example_inited=EFAIL;
Note that the global variable is defined with an error status. When the kernel module is loaded, the callback routine has not yet been called. The callback routine stores its status in this global variable before it returns to the caller. This status is available to the remainder of the source code in the module for the purpose of checking the callback routine status (that is, it checks if the module has been successfully initialized).
2.2.1.3 Allocating Memory for Data Structures
Your kernel module may need to allocate memory for data structures during
initialization.
You must wait until the
CFG_PT_VM_AVAIL
dispatch point occurs.
When you are ready to allocate memory, you use the
MALLOC
macro.
(Use the
FREE
macro to deallocate
memory.) See
Section 5.3.7
for more information about allocating
memory.
2.2.2 Receiving the CFG_OP_UNCONFIGURE Request
The module framework calls the
configure
routine
with the
CFG_OP_UNCONFIGURE
request code to have both statically
loaded and dynamically loaded kernel modules prepare to go off line.
When
modules are brought off line, they are not available for use by any other
module in the kernel.
Only dynamically loaded kernel modules can actually
be unloaded.
Statically loaded modules remain loaded once they are brought
off line.
(See
Section 2.2.1.2
to determine how the kernel module
was loaded.)
When a module (static or dynamic) has successfully gone off line, it
should return
ESUCCESS
.
To prepare to go off line, the kernel module must accomplish the following tasks before returning a success status value to the module framework:
Deallocate all data structures
Deinitialize locks
Terminate all kernel threads
Deregister with other kernel subsystems
A kernel module (static or dynamic) can determine that it cannot be unloaded. In this case, the module should return an error to the module framework to keep it from attempting to unload the module.