Understanding the Linux Kernel Initcall Mechanism for Driver Initialization
The article explains how the Linux kernel’s initcall mechanism automatically orders and executes driver initialization functions through specially defined macros and linker sections, improving maintainability, memory usage, and system stability during the boot process.
Linux devices rely on the kernel to initialize a vast number of hardware drivers, and the initcall mechanism acts as an invisible coordinator that arranges these initializations in a deterministic order.
1. The Problem of Linux Driver Initialization
Drivers can be built statically into the kernel or loaded dynamically. Static drivers require a function such as xxx_init() to be called at a specific boot stage, but manually inserting calls into the kernel’s init routine quickly becomes unmanageable as the number of drivers grows.
A naive approach is to maintain a single list of function pointers and iterate over it during boot, but this still requires manual updates whenever drivers are added or removed, leading to errors and poor maintainability.
2. Initcall Mechanism Appears
2.1 Core Concept
The kernel introduces a set of macros (e.g., early_initcall , core_initcall , device_initcall ) that place driver init functions into dedicated ELF sections. At boot time, the kernel walks these sections in a predefined priority order and calls each function, ensuring a clean and automatic initialization sequence.
2.2 Source Code Analysis
All initcall macros ultimately expand to the __define_initcall macro, which creates a static function pointer placed in a section named .initcall<id>.init and marks it with __used to prevent optimization removal.
#define pure_initcall(fn) __define_initcall(fn, 0)
#define core_initcall(fn) __define_initcall(fn, 1)
#define core_initcall_sync(fn) __define_initcall(fn, 1s)
#define postcore_initcall(fn) __define_initcall(fn, 2)
#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
#define arch_initcall(fn) __define_initcall(fn, 3)
#define arch_initcall_sync(fn) __define_initcall(fn, 3s)
#define subsys_initcall(fn) __define_initcall(fn, 4)
#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
#define fs_initcall(fn) __define_initcall(fn, 5)
#define fs_initcall_sync(fn) __define_initcall(fn, 5s)
#define rootfs_initcall(fn) __define_initcall(fn, rootfs)
#define device_initcall(fn) __define_initcall(fn, 6)
#define device_initcall_sync(fn) __define_initcall(fn, 6s)
#define late_initcall(fn) __define_initcall(fn, 7)
#define late_initcall_sync(fn) __define_initcall(fn, 7s)For static drivers, module_init(xxx_init) expands to device_initcall(xxx_init) , which places the function in the .initcall6.init section.
2.3 Implementation Details
The kernel’s do_initcalls() function iterates over an array initcall_levels[] that holds the start symbols of each initcall section. For each level it calls do_one_initcall() (or the debug variant) on every function pointer found.
static initcall_t *initcall_levels[] __initdata = {
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
};The do_one_initcall() wrapper checks for blacklisted initcalls, optional debugging output, pre‑empt count balance, and IRQ state before finally invoking the driver’s init function.
int __init_or_module do_one_initcall(initcall_t fn)
{
int count = preempt_count();
int ret;
char msgbuf[64];
if (initcall_blacklisted(fn))
return -EPERM;
if (initcall_debug)
ret = do_one_initcall_debug(fn);
else
ret = fn();
/* post‑call checks omitted for brevity */
return ret;
}3. The Secrets of Initcall Priorities
3.1 Priority Rules
Initcalls are divided into eight levels (0‑7). Lower numbers have higher priority and run earlier. For example, pure_initcall (level 0) runs before most other subsystems, while late_initcall (level 7) runs after the system is largely stable.
Special prefixes such as arch_ indicate hardware‑architecture‑specific initialization, and rootfs_initcall handles root‑filesystem setup between levels 5 and 6.
3.2 Practical Example
Assume three drivers: i2c_driver (needs early hardware setup → arch_initcall ), video_driver (depends on memory management → subsys_initcall ), and audio_driver (can be delayed → late_initcall ). The kernel will invoke them in the order dictated by their respective sections.
3.3 Benefits
The initcall system simplifies driver development, reduces manual bookkeeping, frees memory occupied by init code after boot, and guarantees a safe, deterministic initialization order, preventing resource conflicts and crashes.
4. Full Implementation in the Linux Kernel
The header include/linux/init.h defines the macros that mark functions as initcalls. The macro __define_initcall creates a static pointer with the attribute __section__(".initcall" #id ".init") and marks it __used to keep it from being discarded.
#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn; \
LTO_REFERENCE_INITCALL(__initcall_##fn##id)During linking, the linker script (e.g., arch/x86/kernel/vmlinux.lds ) places each of these sections into the .init.data segment, allowing the kernel to locate them via the symbols generated by the script.
.init.data : AT(ADDR(.init.data) - 0xffffffff80000000) {
__initcall_start = .;
*(.initcallearly.init)
__initcall0_start = .;
*(.initcall0.init)
*(.initcall0s.init)
__initcall1_start = .;
...
}When the kernel reaches do_basic_setup() in init/main.c , it calls do_initcalls() , which walks the initcall_levels array and executes each init function in the correct order, completing the boot sequence.
Additional initcall levels such as rootfs_initcall , console_initcall , and the *_sync variants provide finer‑grained control for special cases like root‑filesystem unpacking or synchronized initialization across modules.
Deepin Linux
Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.