When the kernel boots up, it starts as a single thread for which PID = 0. It is named as "swapper". This eventually calls cpu_idle() (which is architecture specific function) and this task is nothing but idle task.
init/main.c
start_kernel(void)
{
...
rest_init();
This is the last function that is called after all inits are called.
}
rest_init()
{
...
/* We need to spawn init first so that it obtains pid 1, however
* the init task will end up wanting to create kthreads, which, if
* we schedule it before we create kthreadd, will OOPS.
*/
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); PID = 1
...
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); PID = 2
...
/*
* The boot idle thread must execute schedule()
* at least once to get things moving:
*/
init_idle_bootup_task(current);
schedule_preempt_disabled();
/* Call into cpu_idle with preempt disabled */
cpu_idle(); PID = 0
}
arch/arm/kernel/process.c
cpu_idle()
{
...
/* endless idle loop with no priority at all */
while (1) {
...
while (!need_resched()) {
...
if (cpuidle_idle_call())
pm_idle()
If CPU_IDLE is enabled, in non-error case, cpuidle_idle_call()
returns 0. Thereby pm_idle() is not called. Determining and
entering the sleep state is done in cpuidle_idle_call()
itself.
...
}
}
}
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
pm_idle()
cpu_do_idle() => cpu_v7_do_idle
arch/arm/mm/proc-v7.S
ENTRY(cpu_v7_do_idle)
dsb @ WFI may enter a low-power mode
wfi
mov pc, lr
ENDPROC(cpu_v7_do_idle)
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
drivers/cpuidle/cpuidle.c
int cpuidle_idle_call(void)
{
...
/* ask the governor for the next state */
next_state = cpuidle_curr_governor->select(drv, dev);
Current cpuidle governer identifies the appropriate idle
state. Platform data is used by the governor to select the
approprite sleep state.
...
entered_state = cpuidle_enter_ops(dev, drv, next_state);
Here we enter the idle state suggested by current cpuidle
governor. Platform specific code comes into picture to set
the appropriate idle state.
...
}
init/main.c
start_kernel(void)
{
...
rest_init();
This is the last function that is called after all inits are called.
}
rest_init()
{
...
/* We need to spawn init first so that it obtains pid 1, however
* the init task will end up wanting to create kthreads, which, if
* we schedule it before we create kthreadd, will OOPS.
*/
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); PID = 1
...
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); PID = 2
...
/*
* The boot idle thread must execute schedule()
* at least once to get things moving:
*/
init_idle_bootup_task(current);
schedule_preempt_disabled();
/* Call into cpu_idle with preempt disabled */
cpu_idle(); PID = 0
}
arch/arm/kernel/process.c
cpu_idle()
{
...
/* endless idle loop with no priority at all */
while (1) {
...
while (!need_resched()) {
...
if (cpuidle_idle_call())
pm_idle()
If CPU_IDLE is enabled, in non-error case, cpuidle_idle_call()
returns 0. Thereby pm_idle() is not called. Determining and
entering the sleep state is done in cpuidle_idle_call()
itself.
...
}
}
}
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
pm_idle()
cpu_do_idle() => cpu_v7_do_idle
arch/arm/mm/proc-v7.S
ENTRY(cpu_v7_do_idle)
dsb @ WFI may enter a low-power mode
wfi
mov pc, lr
ENDPROC(cpu_v7_do_idle)
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
drivers/cpuidle/cpuidle.c
int cpuidle_idle_call(void)
{
...
/* ask the governor for the next state */
next_state = cpuidle_curr_governor->select(drv, dev);
Current cpuidle governer identifies the appropriate idle
state. Platform data is used by the governor to select the
approprite sleep state.
...
entered_state = cpuidle_enter_ops(dev, drv, next_state);
Here we enter the idle state suggested by current cpuidle
governor. Platform specific code comes into picture to set
the appropriate idle state.
...
}
[Based on Linux kernel
v3.4]
No comments:
Post a Comment