enter_state() -->suspend_prepare(state); -->suspend_freeze_processes(); static inline int suspend_freeze_processes(void) { int error; error = freeze_processes(); /* * freeze_processes() automatically thaws every task if freezing * fails. So we need not do anything extra upon error. */ if (error) return error; error = freeze_kernel_threads(); /* * freeze_kernel_threads() thaws only kernel threads upon freezing * failure. So we have to thaw the userspace tasks ourselves. */ if (error) thaw_processes(); return error; }
Free_process is the thread of free userspace, and free_kernel_threads is the thread used for free kernel space
PM ﹣ freezing global variable indicates that userspace begins to enter the freeing stage. First, set the current process to suspend ﹣ task, which is responsible for freeing all other userspace tasks and calling try ﹣ to ﹣ free ﹣ tasks. In this function, suspend ﹣ task will always be in the while sleep. The exit conditions are as follows: once exiting, call that ﹣ process to re solve the free process freeze 1 (!todo || time_after(jiffies, end_time)),freeze_timeout_msecs = 20 * MSEC_PER_SEC , exit if the freeze action is not completed within 20s
2 (pm_wakeup_pending())
freeze_task The function will send a signal to the thread of user space, so that when the suspend task leaves the cpu, other threads will return from the kernel space and need to be processed __refrigerator
int freeze_processes(void) { /* Make sure this task doesn't get frozen */ current->flags |= PF_SUSPEND_TASK; pr_info("Freezing user space processes ... "); pm_freezing = true; error = try_to_freeze_tasks(true); /* * Now that the whole userspace is frozen we need to disbale * the OOM killer to disallow any further interference with * killable tasks. There is no guarantee oom victims will * ever reach a point they go away we have to wait with a timeout. */ if (error) thaw_processes(); return error; } static int try_to_freeze_tasks(bool user_only) { while (true) { todo = 0; for_each_process_thread(g, p) { if (p == current || !freeze_task(p)) continue; if (!freezer_should_skip(p)) todo++; } if (!user_only) { wq_busy = freeze_workqueues_busy(); todo += wq_busy; } if (!todo || time_after(jiffies, end_time)) break; if (pm_wakeup_pending()) { wakeup = true; break; } /* * We need to retry, but first give the freezing tasks some * time to enter the refrigerator. Start with an initial * 1 ms sleep followed by exponential backoff until 8 ms. */ usleep_range(sleep_usecs / 2, sleep_usecs); if (sleep_usecs < 8 * USEC_PER_MSEC) sleep_usecs *= 2; } if (todo) { pr_cont("\n"); pr_err("Freezing of tasks %s after %d.%03d seconds " "(%d tasks refusing to freeze, wq_busy=%d):\n", wakeup ? "aborted" : "failed", elapsed_msecs / 1000, elapsed_msecs % 1000, todo - wq_busy, wq_busy); if (wq_busy) show_workqueue_state(); if (!wakeup) { for_each_process_thread(g, p) { if (p != current && !freezer_should_skip(p) && freezing(p) && !frozen(p)) sched_show_task(p); } } } else { pr_cont("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000, elapsed_msecs % 1000); } return todo ? -EBUSY : 0; }