init function invocation of drivers compiled into kernel
In Linux if device drivers are built as loadable kernel modules, then upon inserting the device driver kernel module, the kernel calls the init function of the device driver as pointed out by module_init()
macro.
How does this work for device drivers that are statically compiled into the kernel ? How is their init function called ?
The init routine of a built-in driver can still use the module_init()
macro to declare that entry point. Or the driver can use device_initcall()
when the driver would never be compiled as a loadable module. Or to move its initialization very early in the boot sequence, the driver could use subsys_initcall()
.
In include/linux/init.h
the sequence for invoking these init routines is described as:
/* initcalls are now grouped by functionality into separate
* subsections. Ordering inside the subsections is determined
* by link order.
* For backwards compatibility, initcall() puts the call in
* the device init subsection.
*
* The `id' arg to __define_initcall() is needed so that multiple initcalls
* can point at the same handler without causing duplicate-symbol build errors.
*/
I assume that these subsections for device drivers correspond to the subdirectories within the drivers
directory of the Linux kernel source tree, and that the link order is recorded in the built-in.o file of each subdirectory in drivers
. So during kernel boot the init routine of each built-in driver is eventually executed by do_initcalls()
in init/main.c
.
The init routine of the device driver is responsible for probing the system to verify that the HW device actually exists. The driver should not allocate any resources or register any devices when the probe fails.
UPDATE:
Passing the option "initcall_debug" on the kernel command line will cause timing information to be printed to the console for each initcall. initcalls are used to initialize statically linked kernel drivers and subsystems and contribute a significant amount of time to the Linux boot process. The output looks like:
calling tty_class_init+0x0/0x44 @ 1
initcall tty_class_init+0x0/0x44 returned 0 after 9765 usecs
calling spi_init+0x0/0x90 @ 1
initcall spi_init+0x0/0x90 returned 0 after 9765 usecs
Reference: http://elinux.org/Initcall_Debug
As specified by the comments in kernel init.h
"module_init() will either be called during do_initcalls() (if builtin) or at module insertion time (if a module)."
If you look into init.h then you will see
define module_init(x) __initcall(x);
And if you closely observe then
define __initcall(fn) device_initcall(fn)
And
define device_initcall(fn) __define_initcall("6",fn,6)
So basically the module init leads to initcall (NOTE: only for statically compiled modules) at the time of boot only.