Android sleep (suspend)
Involved files:
Kernel/power/main. c
Kernel/power/earlysuspend. c
Kernel/power/wakelock. c
There are three main steps for suspend:
1. Freeze user-State processes and kernel-state tasks
2. Call the suspend callback function of the registered device in the order of registration.
3. Sleep the core device and sleep the CPU into a sleep frozen process. The kernel sets the state of all processes in the process list to stop and saves the context of all processes. When these processes are restored, they do not know that they have been restored, but simply continue to execute. We can control the system to sleep through read/write/sys/power/state. For example, echo standby>/sys/power/state
Use cat/sys/power/state
To view the kernel supports several sleep modes.
The user's read/write operations on/sys/power/state call main. state_store () in c. You can write strings defined in const char * const pm_state [], such as "mem", "standby ". then state_store () will call enter_state (). It will first check some status parameters and then synchronize the file system.
enter_state - Do common work of entering low-power state.
* @state: pm_state structure for state we're entering.
*
* Make sure we're the only ones trying to enter a sleep state. Fail
* if someone has beat us to it, since we don't want anything weird to
* happen when we wake up.
* Then, do the setup for suspend, enter the state, and cleaup (after
* we've woken up).
*/
static int enter_state(suspend_state_t state)
{
int error;
if (!valid_state(state))
return -ENODEV;
if (!mutex_trylock(&pm_mutex))
return -EBUSY;
printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync();
printk("done./n");
pr_debug("PM: Preparing system for %s sleep/n", pm_states[state]);
error = suspend_prepare();
if (error)
goto Unlock;
if (suspend_test(TEST_FREEZER))
goto Finish;
pr_debug("PM: Entering %s sleep/n", pm_states[state]);
error = suspend_devices_and_enter(state);
Finish:
pr_debug("PM: Finishing wakeup./n");
suspend_finish();
Unlock:
mutex_unlock(&pm_mutex);
return error;
}
After entering suspend_prepare (), it will allocate a virtual terminal to suspend to output the message, then broadcast a system to access suspend's Notify y and disable the user.
State helper process, and then call suspend_freeze_processes () to freeze all processes. The current state of all processes will be saved here, and some processes may refuse to enter
Freezing status. If a process exists, it will cause freezing failure. This function will discard the freezing process and unfreeze all the frozen processes.
Sleep peripheralsNow, all processes (including workqueue/kthread) have stopped, and kernel-state characters may hold some semaphores when they are stopped, so if they are in the peripherals at this time
Unlocking this signal may cause deadlocks, so be very careful when locking/unlocking locks in the suspend () function of the peripherals. We recommend that you do not lock the lock in suspend () during design.
This is because some logs cannot be output during suspend, so it is very difficult to debug the suspend.
Then the kernel will try to release some memory here.
At last, we will call suspend_devices_and_enter () to sleep all peripherals. In this function, if the platform registers suspend_pos (usually defined and registered in the board-level definition ), here we will call suspend_ops-> begin (), and then driver/base/power/main. in c, device_suspend ()-> dpm_suspend () will be called, and they will call the suspend () callback of the driver in sequence to sleep all devices.
When all devices sleep, suspend_ops-> prepare () will be called. This function usually makes some preparations to let the machine sleep. in Linux, the non-startup CPU in the multi-core CPU will be turned off. The comment shows that it is to avoid these other CPUs from causing race condion. In the future, only one CPU is running.
Next, suspend_enter () will be called. This function will disable arch irq and call device_power_down (). It will call the suspend_late () function, this function is the final function called by the system to sleep. It usually performs the final check in this function. if the check is correct, sleep all the system devices and buses and call suspend_pos-> enter () to enable the CPU to power down. at this time, it is sleep. code execution stops here.
/**
* suspend_devices_and_enter - suspend devices and enter the desired system
* sleep state.
* @state: state to enter
*/
int suspend_devices_and_enter(suspend_state_t state)
{
int error, ftrace_save;
if (!suspend_ops)
return -ENOSYS;
if (suspend_ops->begin) {
error = suspend_ops->begin(state);
if (error)
goto Close;
}
suspend_console();
ftrace_save = __ftrace_enabled_save();
suspend_test_start();
error = device_suspend(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM: Some devices failed to suspend/n");
goto Recover_platform;
}
suspend_test_finish("suspend devices");
if (suspend_test(TEST_DEVICES))
goto Recover_platform;
if (suspend_ops->prepare) {
error = suspend_ops->prepare();
if (error)
goto Resume_devices;
}
if (suspend_test(TEST_PLATFORM))
goto Finish;
error = disable_nonboot_cpus();
if (!error && !suspend_test(TEST_CPUS))
suspend_enter(state);
enable_nonboot_cpus();
Finish:
if (suspend_ops->finish)
suspend_ops->finish();
Resume_devices:
suspend_test_start();
device_resume(PMSG_RESUME);
suspend_test_finish("resume devices");
__ftrace_enabled_restore(ftrace_save);
resume_console();
Close:
if (suspend_ops->end)
suspend_ops->end();
return error;
Recover_platform:
if (suspend_ops->recover)
suspend_ops->recover();
goto Resume_devices;
}
Term
Early Suspend: Suspend is used to disable display-related devices after the screen lock, such as LCD, light sensor, and Touch panel. At this time, other tasks are running, such as SD card files.
Late Resume: Late Resume is a mechanism supporting suspend. It is executed after kernel wake-up is completed. It is mainly used to wake up devices that sleep during Early Suspend.
Wake Lock: The Wake Lock is a Lock mechanism. As long as someone holds the Lock, the system cannot enter sleep and can be obtained by user-State processes and kernels. The lock can be time-out or time-out. The time-out lock will be automatically unlocked after the time expires. If no lock or time-out occurs, the kernel starts the sleep mechanism to enter sleep.
Android Suspend
When the user writes mem or standby to/sys/power/state, state_store () will be called, and Android will call request_suspend_state () in philosophy (), standard linux will enter the enter_state () function here. If the request is sleep, early_suspend
Zhege workqueue is called and enters the early_suspend state.
Void request_suspend_state (suspend_state_t new_state)
{
Unsigned long irqflags;
Int old_sleep;
Spin_lock_irqsave (& state_lock, irqflags );
Old_sleep = state & SUSPEND_REQUESTED;
If (debug_mask & DEBUG_USER_STATE ){
Struct timespec ts;
Struct rtc_time tm;
Getnstimeofday (& ts );
Rtc_time_to_tm (ts. TV _sec, & tm );
Pr_info ("request_suspend_state: % s (% d-> % d) at % lld"
"(% D-% 02d-% 02d % 02d: % 02d: % 02d. % 09lu UTC)/n ",
New_state! = PM_SUSPEND_ON? "Sleep": "wakeup ",
Requested_suspend_state, new_state,
Ktime_to_ns (ktime_get ()),
TM. tm_year + 1900, TM. tm_mon + 1, TM. tm_mday,
TM. tm_hour, TM. tm_min, TM. tm_sec, ts. TV _nsec );
}
If (! Old_sleep & new_state! = Pm_suspend_on ){
State | = suspend_requested;
Queue_work (suspend_work_queue, & early_suspend_work );
} Else if (old_sleep & new_state = pm_suspend_on ){
State & = ~ Suspend_requested;
Wake_lock (& main_wake_lock );
Queue_work (suspend_work_queue, & late_resume_work );
}
Requested_suspend_state = new_state;
Spin_unlock_irqrestore (& state_lock, irqflags );
}
Early suspend
In the early_suspend () function, the system first checks whether the current request status is suspend, this prevents suspend requests from being canceled at this time (because the user process is still running at this time). If it needs to be released, it simply exits. If no, this function will call a series of events registered in eralysuspend once, synchronize the file system, and then discard main_wake_lock. The wake lock is a lock without timeout, if the lock is not released, the system will not be able to sleep.
Static void early_suspend (struct work_struct * work)
{
Struct early_suspend * pos;
Unsigned long irqflags;
Int abort = 0;
Mutex_lock (& early_suspend_lock );
Spin_lock_irqsave (& state_lock, irqflags );
If (state = SUSPEND_REQUESTED)
State | = susponded;
Else
Abort = 1;
Spin_unlock_irqrestore (& state_lock, irqflags );
If (abort ){
If (debug_mask & DEBUG_SUSPEND)
Pr_info ("early_suspend: abort, state % d/n", state );
Mutex_unlock (& early_suspend_lock );
Goto abort;
}
If (debug_mask & DEBUG_SUSPEND)
Pr_info ("early_suspend: call handlers/n ");
List_for_each_entry (pos, & early_suspend_handlers, link ){
If (pos-> suspend! = NULL)
Pos-> suspend (pos );
}
Mutex_unlock (& early_suspend_lock );
If (debug_mask & DEBUG_SUSPEND)
Pr_info ("early_suspend: sync/n ");
Sys_sync ();
Abort:
Spin_lock_irqsave (& state_lock, irqflags );
If (state = SUSPEND_REQUESTED_AND_SUSPENDED)
Wake_unlock (& main_wake_lock );
Spin_unlock_irqrestore (& state_lock, irqflags );
}
Late resume
After all the wake-up operations are completed, user processes have started to run. Wake-up is usually due to the following reasons:
. Incoming call
If a call is made, the device remotely calls poweranagerervice to write "on" to/sys/power/State to execute late resume, such as lighting up the screen. Late resume will wake up the device that previously called early suspend.
Static void late_resume (struct work_struct * Work)
{
Struct early_suspend * Pos;
Unsigned long irqflags;
Int abort = 0;
Mutex_lock (& early_suspend_lock );
Spin_lock_irqsave (& state_lock, irqflags );
If (State = susponded)
State & = ~ Suincluded;
Else
Abort = 1;
Spin_unlock_irqrestore (& state_lock, irqflags );
If (abort ){
If (debug_mask & DEBUG_SUSPEND)
Pr_info ("late_resume: abort, state % d/n", state );
Goto abort;
}
If (debug_mask & DEBUG_SUSPEND)
Pr_info ("late_resume: call handlers/n ");
List_for_each_entry_reverse (pos, & early_suspend_handlers, link)
If (pos-> resume! = NULL)
Pos-> resume (pos );
If (debug_mask & DEBUG_SUSPEND)
Pr_info ("late_resume: done/n ");
Abort:
Mutex_unlock (& early_suspend_lock );
}
Wake Lock PATH: wakelock. c
A wake lock has two statuses: locking and unlocking. One is a permanent lock. Such a lock will not be unlocked unless it is displayed, therefore, this kind of lock is very careful. The second is the timeout lock, which will lock the system for a period of time. If the time passes, the lock will be automatically lifted.
There are two types of locks:
1. The lock_suspend lock will prevent the system from sleep.
2. The lock_idle will not affect the sleep of the system.
In the wake lock, there are three places for the system to directly start suspend (), which are
1. In wake_unlock (), if no other wake lock is found after unlocking, sleep begins.
2. After the timer has reached the time, the timer's callback function will check whether there are other wakelock. If not, let the system go to sleep.
3. In wake_lock, after a wake lock is locked, the system checks whether there is a lock again.
Suspend
After wake_lock runs suspend. c's suspend () function will be called. This function first synchronizes the file system and then calls pm_suspend () request_suspend_state). Then pm_suspend () will call enter_state () to enter the Linux sleep process...
static void suspend(struct work_struct *work)
{
int ret;
int entry_event_num;
if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
if (debug_mask & DEBUG_SUSPEND)
pr_info("suspend: abort suspend/n");
return;
}
entry_event_num = current_event_num;
sys_sync();
if (debug_mask & DEBUG_SUSPEND)
pr_info("suspend: enter suspend/n");
ret = pm_suspend(requested_suspend_state);
if (current_event_num == entry_event_num) {
wake_lock_timeout(&unknown_wakeup, HZ / 2);
}
}
ResumeIf the system is interrupted or other events are awakened during sleep, the following code starts to run. The wake-up sequence is the opposite to that of sleep, therefore, the system device and bus will first wake up, enable the system to be disconnected, enable non-startup CPU to be stopped during sleep, and call suspend_ops-> finish (), and in suspend_devices_and_enter () the function will also wake up each device, enable the virtual terminal, and finally call suspend_ops-> end ().
In the return to the enter_state () function, when suspend_devices_and_enter () returns, the peripherals are awakened, but the processes and tasks are still frozen. suspend_finish () is called here () to unfreeze these processes and tasks, and send a running y to indicate that the system has exited from the suspend status and wakened the terminal.
By now, all sleep and wakeup have been completed, and the system continues to run.