The SYSINIT Framework
SYSINIT is the framework for a generic call sort and dispatch
mechanism. FreeBSD currently uses it for the dynamic
initialization of the kernel. SYSINIT allows FreeBSD's kernel
subsystems to be reordered, and added, removed, and replaced at
kernel link time when the kernel or one of its modules is loaded
without having to edit a statically ordered initialization routing
and recompile the kernel. This system also allows kernel modules,
currently called KLD's, to be separately
compiled, linked, and initialized at boot time and loaded even
later while the system is already running. This is accomplished
using the kernel linker
and linker
sets
.
Terminology
Linker Set
A linker technique in which the linker gathers
statically declared data throughout a program's source files
into a single contiguously addressable unit of
data.
SYSINIT Operation
SYSINIT relies on the ability of the linker to take static
data declared at multiple locations throughout a program's
source and group it together as a single contiguous chunk of
data. This linker technique is called a linker
set
. SYSINIT uses two linker sets to maintain two data
sets containing each consumer's call order, function, and a
pointer to the data to pass to that function.
SYSINIT uses two priorities when ordering the functions for
execution. The first priority is a subsystem ID giving an
overall order for SYSINIT's dispatch of functions. Current predeclared
ID's are in <sys/kernel.h> in the enum
list sysinit_sub_id. The second priority used
is an element order within the subsystem. Current predeclared
subsystem element orders are in
<sys/kernel.h> in the enum list
sysinit_elem_order.
There are currently two uses for SYSINIT. Function dispatch
at system startup and kernel module loads, and function dispatch
at system shutdown and kernel module unload. Kernel subsystems
often use system startup SYSINIT's to initialize data
structures, for example the process scheduling subsystem
uses a SYSINIT to initialize the run queue data structure.
Device drivers should avoid using SYSINIT()
directly. Instead drivers for real devices that are part of a
bus structure should use DRIVER_MODULE() to
provide a function that detects the device and, if it is present,
initializes the device. It will do a few things specific to
devices and then call SYSINIT() itself.
For pseudo-devices, which are not part of a bus structure,
use DEV_MODULE().
Using SYSINIT
Interface
Headers
<sys/kernel.h>
Macros
SYSINIT(uniquifier, subsystem, order, func, ident)
SYSUNINIT(uniquifier, subsystem, order, func, ident)
Startup
The SYSINIT() macro creates the
necessary SYSINIT data in SYSINIT's startup data set for
SYSINIT to sort and dispatch a function at system startup and
module load. SYSINIT() takes a uniquifier
that SYSINIT uses to identify the particular function dispatch
data, the subsystem order, the subsystem element order, the
function to call, and the data to pass the function. All
functions must take a constant pointer argument.
Example of a SYSINIT()
#include <sys/kernel.h>
void foo_null(void *unused)
{
foo_doo();
}
SYSINIT(foo, SI_SUB_FOO, SI_ORDER_FOO, foo_null, NULL);
struct foo foo_voodoo = {
FOO_VOODOO;
}
void foo_arg(void *vdata)
{
struct foo *foo = (struct foo *)vdata;
foo_data(foo);
}
SYSINIT(bar, SI_SUB_FOO, SI_ORDER_FOO, foo_arg, &foo_voodoo);
Note that SI_SUB_FOO and
SI_ORDER_FOO need to be in the
sysinit_sub_id and
sysinit_elem_order enum's as mentioned
above. Either use existing ones or add your own to the
enum's. You can also use math for fine-tuning the order
a SYSINIT will run in. This example shows a SYSINIT that
needs to be run just barely before the SYSINIT's that
handle tuning kernel parameters.
Example of Adjusting SYSINIT() Order
static void
mptable_register(void *dummy __unused)
{
apic_register_enumerator(&mptable_enumerator);
}
SYSINIT(mptable_register, SI_SUB_TUNABLES - 1, SI_ORDER_FIRST,
mptable_register, NULL);
Shutdown
The SYSUNINIT() macro behaves similarly
to the SYSINIT() macro except that it adds
the SYSINIT data to SYSINIT's shutdown data set.
Example of a SYSUNINIT()
#include <sys/kernel.h>
void foo_cleanup(void *unused)
{
foo_kill();
}
SYSUNINIT(foobar, SI_SUB_FOO, SI_ORDER_FOO, foo_cleanup, NULL);
struct foo_stack foo_stack = {
FOO_STACK_VOODOO;
}
void foo_flush(void *vdata)
{
}
SYSUNINIT(barfoo, SI_SUB_FOO, SI_ORDER_FOO, foo_flush, &foo_stack);