kernel __init calls

Introduction

The Linux kernel uses a clever and well-optimized mechanism for calling initialization code in drivers and kernel services using __init calls . It’s clever because its functionality is largely abstracted from the driver developer, and it’s well-optimized because after initialization, memory containing the initialization code is released. This article explores how this mechanism works.

Every driver has an entry point function, minimally defined as:

static __init mydriver_init( void )
{
  return platform_driver_register(&my_driver);
}
module_init(mydriver_init);

The __init macro is used to describe the function as only being required during initialization time, so that the kernel can remove this function and release its memory after initialization. To recover memory used by initialization data we use the __initdata macro.

The single module_init macro is used to tell the kernel where the initialisation entry point to the module lives.

In include/linux/init.h:

#define __init __section(.init.text) __cold

And looking in various include/linux/compiler*.h files

compiler.h: 182: #define __section(S) __attribute__ ((__section__(#S)))
compiler-gcc4.h: #define __cold __attribute__ ((cold))

And when we expand it out we get

#define __init __attribute__((__section__(".init.text"))) __attribute__ ((cold))

The cold attribute (since GCC4.3) marks the function as one that is rarely used, which results in the compiler optimizing the function for size instead of speed. The section attribute informs the compiler to put the text for this function in a special section named “.init.text”. Grouping initialisation functions in a single ELF section allows for the entire section to be removed after initialization has been performed

module_init has different behavior depending on whether the module is built-in or not. Let’s consider the built-in case:

#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall __define_initcall("6", fn, 6)
#define __define_initcall(level, fn, id) 
static initcall_t __initcall_##fn##id __used __attribute__ 
    ((__section__(".initcall" level ".init"))) = fn

Which extends to:

#define module_init(x) static initcall_t __initcall_x6 __used __attribute__ 
    ((__section(".initcall6.init"))) = x;
static initcall_t __initcall_mydriver_init6 __used __attribute__ 
    ((__section(".initcall6.init"))) = mydriver_init;

So it declares a function pointer with a unique name to our point of entry and locates it in a special section of the ELF

In addition, during link time the include/asm-generic/vmlinux.lds.h and arch/*/kernel/vmlinux.lds.S scripts ensure that some labels/symbols surround the start and end of these ELF sections.

I.e. __early_initcall_end and __initcall_end mark the start and end of the function pointers and __init_begin and __init_end mark the start and end of the .init.text section.

The function do_initcalls in init/main.c is called during kernel startup. This is shown below:

static void __init do_initcalls(void)
{
initcall_t *call;
for (call = __early_initcall_end; call < __initcall_end; call++)
    do_one_initcall(*call);
}

The purpose of this loop is to execute each of the init functions as set up by the module_init macros. Initially, the function pointer is pointing to the label at the start of our function pointer’s ELF section, and is incremented until the end of the ELF section is reached. For each step, the pointer is invoked and the init function is thus executed.

See the include file: include/linux/init.h for a list of the macros and their ordering.

The order specified there is:

  • early_initcall
  • pure_initcall
  • core_initcall
  • postcore_initcall
  • arch_initcall
  • subsys_initcall
  • fs_initcall
  • rootfs_initcall
  • device_initcall
  • late_initcall
The macros are used to build a table of function pointers for each phase, which are called in sequence by do_initcalls()Once initialization is complete, a function found in the architecture-specific code named free_initmem is used to release the memory pages taken up by the initialization functions and data.

Tags: ,

One Response to “kernel __init calls”

  1. MD ISTAYAQUE says:

    This is Md istayaque Aug. 2012 batch student .(discontinue due to some reason and will continue soon)
    I want to know that how to use llseek in kernel programming
    Its use and its properties

    Thanku .

    Current score: 0

Leave a Reply